From 7cda3d08e4867352cca5c52f7766e04daa73d029 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Mon, 22 Jan 2024 13:15:54 +0000 Subject: [PATCH 001/102] feat: added support for private registries on docker (#2058) This adds support for private container registries TODO - [x] add docs - [x] cleanup all the nils and maybe make the FetchImage interface nicer - [x] perhaps add e2e tests (skipped) Closes #1342 --------- Co-authored-by: Tedi Mitiku --- .../docker_kurtosis_backend.go | 5 +- ...urtosis_backend_api_container_functions.go | 5 +- .../create_and_start_container_args.go | 10 ++ .../docker/docker_manager/docker_manager.go | 49 +++-- .../kubernetes_kurtosis_backend.go | 3 +- .../kubernetes_manager/kubernetes_manager.go | 44 ++--- .../metrics_reporting_kurtosis_backend.go | 5 +- .../lib/backend_interface/kurtosis_backend.go | 3 +- .../mock_kurtosis_backend.go | 37 ++-- .../image_registry_spec.go | 63 +++++++ .../objects/service/service_config.go | 12 ++ .../objects/service/service_config_test.go | 15 +- .../objects/service_user/service_user.go | 53 ++++-- .../service_registration/repository_test.go | 1 + .../default_service_network_test.go | 1 + .../startosis_engine/kurtosis_builtins.go | 1 + .../add_service/add_service_shared.go | 3 + .../add_service/add_service_shared_test.go | 4 + .../tasks/tasks_shared.go | 1 + .../test_engine/add_service_framework_test.go | 1 + .../add_services_framework_test.go | 2 + .../service_config_image_build_spec_test.go | 1 + ...service_config_image_registry_spec_test.go | 82 +++++++++ .../service_config_minimal_framework_test.go | 1 + .../test_engine/static_constants.go | 4 + .../service_config/image_registry_spec.go | 167 ++++++++++++++++++ .../service_config/service_config.go | 25 ++- .../startosis_validator/images_validator.go | 9 +- .../validator_environment.go | 11 +- .../starlark-reference/service-config.md | 21 +++ 30 files changed, 552 insertions(+), 87 deletions(-) create mode 100644 container-engine-lib/lib/backend_interface/objects/image_registry_spec/image_registry_spec.go create mode 100644 core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go create mode 100644 core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_registry_spec.go diff --git a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go index d57c539b47..d5ac56df8e 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go @@ -3,6 +3,7 @@ package docker_kurtosis_backend import ( "context" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "io" "sync" @@ -84,8 +85,8 @@ func NewDockerKurtosisBackend( } } -func (backend *DockerKurtosisBackend) FetchImage(ctx context.Context, image string, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { - return backend.dockerManager.FetchImage(ctx, image, downloadMode) +func (backend *DockerKurtosisBackend) FetchImage(ctx context.Context, image string, registrySpec *image_registry_spec.ImageRegistrySpec, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { + return backend.dockerManager.FetchImage(ctx, image, registrySpec, downloadMode) } func (backend *DockerKurtosisBackend) PruneUnusedImages(ctx context.Context) ([]string, error) { diff --git a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend_api_container_functions.go b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend_api_container_functions.go index 0693f4ed40..c39caf00ce 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend_api_container_functions.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend_api_container_functions.go @@ -2,6 +2,7 @@ package docker_kurtosis_backend import ( "context" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "net" "time" @@ -34,6 +35,8 @@ const ( // TODO: MIGRATE THIS FOLDER TO USE STRUCTURE OF USER_SERVICE_FUNCTIONS MODULE +var emptyRegistrySpecAsPublicImage *image_registry_spec.ImageRegistrySpec = nil + func (backend *DockerKurtosisBackend) CreateAPIContainer( ctx context.Context, image string, @@ -183,7 +186,7 @@ func (backend *DockerKurtosisBackend) CreateAPIContainer( labelStrs, ).WithRestartPolicy(docker_manager.RestartOnFailure).Build() - if _, err = backend.dockerManager.FetchImageIfMissing(ctx, image); err != nil { + if _, err = backend.dockerManager.FetchImageIfMissing(ctx, image, emptyRegistrySpecAsPublicImage); err != nil { logrus.Warnf("Failed to pull the latest version of API container image '%v'; you may be running an out-of-date version. Error:\n%v", image, err) } diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/create_and_start_container_args.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/create_and_start_container_args.go index 9a1f40c96b..4bc25bf54e 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_manager/create_and_start_container_args.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/create_and_start_container_args.go @@ -1,6 +1,7 @@ package docker_manager import ( + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user" "net" @@ -34,6 +35,7 @@ type CreateAndStartContainerArgs struct { restartPolicy RestartPolicy imageDownloadMode image_download_mode.ImageDownloadMode user *service_user.ServiceUser + imageRegistrySpec *image_registry_spec.ImageRegistrySpec } // Builder for creating CreateAndStartContainerArgs object @@ -62,6 +64,7 @@ type CreateAndStartContainerArgsBuilder struct { restartPolicy RestartPolicy imageDownloadMode image_download_mode.ImageDownloadMode user *service_user.ServiceUser + imageRegistrySpec *image_registry_spec.ImageRegistrySpec } /* @@ -97,6 +100,7 @@ func NewCreateAndStartContainerArgsBuilder(dockerImage string, name string, netw restartPolicy: NoRestart, imageDownloadMode: image_download_mode.ImageDownloadMode_Missing, user: nil, + imageRegistrySpec: nil, } } @@ -126,6 +130,7 @@ func (builder *CreateAndStartContainerArgsBuilder) Build() *CreateAndStartContai restartPolicy: builder.restartPolicy, imageDownloadMode: builder.imageDownloadMode, user: builder.user, + imageRegistrySpec: builder.imageRegistrySpec, } } @@ -275,3 +280,8 @@ func (builder *CreateAndStartContainerArgsBuilder) WithUser(user *service_user.S builder.user = user return builder } + +func (builder *CreateAndStartContainerArgsBuilder) WithImageRegistrySpec(imageRegistrySpec *image_registry_spec.ImageRegistrySpec) *CreateAndStartContainerArgsBuilder { + builder.imageRegistrySpec = imageRegistrySpec + return builder +} diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go index 6bcdcdcdba..75abbb375a 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/api/types/registry" "github.com/docker/go-units" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/uuid_generator" "github.com/kurtosis-tech/kurtosis/utils" "io" @@ -527,7 +528,7 @@ func (manager *DockerManager) CreateAndStartContainer( dockerImage = dockerImage + dockerTagSeparatorChar + dockerDefaultTag } - _, _, err := manager.FetchImage(ctx, dockerImage, args.imageDownloadMode) + _, _, err := manager.FetchImage(ctx, dockerImage, args.imageRegistrySpec, args.imageDownloadMode) if err != nil { logrus.Debugf("Error occurred fetching image '%v'. Err:\n%v", dockerImage, err) return "", nil, stacktrace.Propagate(err, "An error occurred fetching image '%v'", dockerImage) @@ -1223,7 +1224,7 @@ func (manager *DockerManager) GetContainersByNetworkId(ctx context.Context, netw // [FetchImageIfMissing] uses the local [dockerImage] if it's available. // If unavailable, will attempt to fetch the latest image. // Returns error if local [dockerImage] is unavailable and pulling image fails. -func (manager *DockerManager) FetchImageIfMissing(ctx context.Context, dockerImage string) (bool, error) { +func (manager *DockerManager) FetchImageIfMissing(ctx context.Context, dockerImage string, registrySpec *image_registry_spec.ImageRegistrySpec) (bool, error) { // if the image name doesn't have version information we concatenate `:latest` // this behavior is similar to CreateAndStartContainer above // this allows us to be deterministic in our behaviour @@ -1239,7 +1240,7 @@ func (manager *DockerManager) FetchImageIfMissing(ctx context.Context, dockerIma if !doesImageExistLocally { logrus.Tracef("Image doesn't exist locally, so attempting to pull it...") - err = manager.pullImage(ctx, dockerImage) + err = manager.pullImage(ctx, dockerImage, registrySpec) if err != nil { return false, stacktrace.Propagate(err, "Failed to pull Docker image '%v' from remote image repository", dockerImage) } @@ -1252,7 +1253,7 @@ func (manager *DockerManager) FetchImageIfMissing(ctx context.Context, dockerIma // [FetchLatestImage] always attempts to retrieve the latest [dockerImage]. // If retrieving the latest [dockerImage] fails, the local image will be used. // Returns error, if no local image is available after retrieving latest fails. -func (manager *DockerManager) FetchLatestImage(ctx context.Context, dockerImage string) error { +func (manager *DockerManager) FetchLatestImage(ctx context.Context, dockerImage string, registrySpec *image_registry_spec.ImageRegistrySpec) error { // if the image name doesn't have version information we concatenate `:latest` // this behavior is similar to CreateAndStartContainer above // this allows us to be deterministic in our behaviour @@ -1269,14 +1270,14 @@ func (manager *DockerManager) FetchLatestImage(ctx context.Context, dockerImage // try and pull latest image even if image exists locally if doesImageExistLocally { logrus.Tracef("Image exists locally, but attempting to get latest from remote image repository.") - err = manager.pullImage(ctx, dockerImage) + err = manager.pullImage(ctx, dockerImage, registrySpec) if err != nil { logrus.Tracef("Failed to pull Docker image '%v' from remote image repository. Going to use available local image.", dockerImage) } else { logrus.Tracef("Latest image successfully pulled from remote to local.") } } else { - err = manager.pullImage(ctx, dockerImage) + err = manager.pullImage(ctx, dockerImage, registrySpec) if err != nil { return stacktrace.Propagate(err, "Failed to pull Docker image '%v' from remote image repository.", dockerImage) } @@ -1285,16 +1286,16 @@ func (manager *DockerManager) FetchLatestImage(ctx context.Context, dockerImage return nil } -func (manager *DockerManager) FetchImage(ctx context.Context, image string, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { +func (manager *DockerManager) FetchImage(ctx context.Context, image string, registrySpec *image_registry_spec.ImageRegistrySpec, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { var err error var pulledFromRemote bool = true logrus.Debugf("Fetching image '%s' with image download mode: %s", image, downloadMode) switch image_fetching := downloadMode; image_fetching { case image_download_mode.ImageDownloadMode_Always: - err = manager.FetchLatestImage(ctx, image) + err = manager.FetchLatestImage(ctx, image, registrySpec) case image_download_mode.ImageDownloadMode_Missing: - pulledFromRemote, err = manager.FetchImageIfMissing(ctx, image) + pulledFromRemote, err = manager.FetchImageIfMissing(ctx, image, registrySpec) default: return false, "", stacktrace.NewError("Undefined image pulling mode: '%v'", image_fetching) } @@ -1546,14 +1547,14 @@ func (manager *DockerManager) isImageAvailableLocally(imageName string) (bool, e return numMatchingImages > 0, nil } -func (manager *DockerManager) pullImage(context context.Context, imageName string) error { +func (manager *DockerManager) pullImage(context context.Context, imageName string, registrySpec *image_registry_spec.ImageRegistrySpec) error { // As we're using the docker client with no timeout to pull the image, we quickly check with the client that has // a timeout whether the docker engine is reachable. if _, err := manager.dockerClient.Ping(context); err != nil { return stacktrace.Propagate(err, "An error occurred communicating with docker engine") } logrus.Infof("Pulling image '%s'", imageName) - err, retryWithLinuxAmd64 := pullImage(manager.dockerClientNoTimeout, imageName, defaultPlatform) + err, retryWithLinuxAmd64 := pullImage(manager.dockerClientNoTimeout, imageName, registrySpec, defaultPlatform) if err == nil { return nil } @@ -1562,7 +1563,7 @@ func (manager *DockerManager) pullImage(context context.Context, imageName strin } // we retry with linux/amd64 logrus.Debugf("Retrying pulling image '%s' for '%s'", imageName, linuxAmd64) - err, _ = pullImage(manager.dockerClientNoTimeout, imageName, linuxAmd64) + err, _ = pullImage(manager.dockerClientNoTimeout, imageName, registrySpec, linuxAmd64) if err != nil { return stacktrace.Propagate(err, "Had previously failed with a manifest error so tried pulling image '%v' for platform '%v' but failed", imageName, linuxAmd64) } @@ -2170,17 +2171,35 @@ func getEndpointSettingsForIpAddress(ipAddress string, alias string) *network.En return config } -func pullImage(dockerClient *client.Client, imageName string, platform string) (error, bool) { +func pullImage(dockerClient *client.Client, imageName string, registrySpec *image_registry_spec.ImageRegistrySpec, platform string) (error, bool) { // Own context for pulling images because we do not want to cancel this works in case the main context in the request is cancelled // if the fist request fails the image will be ready for following request making the process faster pullImageCtx := context.Background() logrus.Tracef("Starting pulling '%s' for platform '%s'", imageName, platform) - out, err := dockerClient.ImagePull(pullImageCtx, imageName, types.ImagePullOptions{ + imagePullOptions := types.ImagePullOptions{ All: false, RegistryAuth: "", PrivilegeFunc: nil, Platform: platform, - }) + } + if registrySpec != nil { + authConfig := registry.AuthConfig{ + Username: registrySpec.GetUsername(), + Password: registrySpec.GetPassword(), + Email: "", + Auth: "", + ServerAddress: registrySpec.GetRegistryAddr(), + IdentityToken: "", + RegistryToken: "", + } + encodedAuthConfig, err := registry.EncodeAuthConfig(authConfig) + if err != nil { + return stacktrace.Propagate(err, "An error occurred while converting registry auth to base64"), false + } + imagePullOptions.RegistryAuth = encodedAuthConfig + + } + out, err := dockerClient.ImagePull(pullImageCtx, imageName, imagePullOptions) if err != nil { return stacktrace.Propagate(err, "Tried pulling image '%v' with platform '%v' but failed", imageName, platform), false } diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go index 68f0c109cc..43e1c4b1fb 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go @@ -3,6 +3,7 @@ package kubernetes_kurtosis_backend import ( "context" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "io" apiv1 "k8s.io/api/core/v1" @@ -114,7 +115,7 @@ func NewCLIModeKubernetesKurtosisBackend( ) } -func (backend *KubernetesKurtosisBackend) FetchImage(ctx context.Context, image string, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { +func (backend *KubernetesKurtosisBackend) FetchImage(ctx context.Context, image string, registrySpec *image_registry_spec.ImageRegistrySpec, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { logrus.Warnf("FetchImage isn't implemented for Kubernetes yet") return false, "", nil } diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go index b7073c6b45..a5f56e6a88 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go @@ -1089,27 +1089,29 @@ func (manager *KubernetesManager) CreatePod( HostIPC: false, ShareProcessNamespace: nil, SecurityContext: nil, - ImagePullSecrets: nil, - Hostname: "", - Subdomain: "", - Affinity: nil, - SchedulerName: "", - Tolerations: nil, - HostAliases: nil, - PriorityClassName: "", - Priority: nil, - DNSConfig: nil, - ReadinessGates: nil, - RuntimeClassName: nil, - EnableServiceLinks: nil, - PreemptionPolicy: nil, - Overhead: nil, - TopologySpreadConstraints: nil, - SetHostnameAsFQDN: nil, - OS: nil, - HostUsers: nil, - SchedulingGates: nil, - ResourceClaims: nil, + // TODO add support for ImageRegistrySpec to Kubernetes by adding the right secret here + // You will have to first publish the secret using the Kubernetes API + ImagePullSecrets: nil, + Hostname: "", + Subdomain: "", + Affinity: nil, + SchedulerName: "", + Tolerations: nil, + HostAliases: nil, + PriorityClassName: "", + Priority: nil, + DNSConfig: nil, + ReadinessGates: nil, + RuntimeClassName: nil, + EnableServiceLinks: nil, + PreemptionPolicy: nil, + Overhead: nil, + TopologySpreadConstraints: nil, + SetHostnameAsFQDN: nil, + OS: nil, + HostUsers: nil, + SchedulingGates: nil, + ResourceClaims: nil, } podToCreate := &apiv1.Pod{ diff --git a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go index b9d4a07360..27fed58069 100644 --- a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go @@ -3,6 +3,7 @@ package metrics_reporting import ( "context" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "io" "time" @@ -29,8 +30,8 @@ func NewMetricsReportingKurtosisBackend(underlying backend_interface.KurtosisBac return &MetricsReportingKurtosisBackend{underlying: underlying} } -func (backend *MetricsReportingKurtosisBackend) FetchImage(ctx context.Context, image string, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { - pulledFromRemote, architecture, err := backend.underlying.FetchImage(ctx, image, downloadMode) +func (backend *MetricsReportingKurtosisBackend) FetchImage(ctx context.Context, image string, registrySpec *image_registry_spec.ImageRegistrySpec, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { + pulledFromRemote, architecture, err := backend.underlying.FetchImage(ctx, image, registrySpec, downloadMode) if err != nil { return false, "", stacktrace.Propagate(err, "An error occurred pulling image '%v'", image) } diff --git a/container-engine-lib/lib/backend_interface/kurtosis_backend.go b/container-engine-lib/lib/backend_interface/kurtosis_backend.go index bc85bb5ebe..06da782443 100644 --- a/container-engine-lib/lib/backend_interface/kurtosis_backend.go +++ b/container-engine-lib/lib/backend_interface/kurtosis_backend.go @@ -3,6 +3,7 @@ package backend_interface import ( "context" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "io" "time" @@ -34,7 +35,7 @@ type KurtosisBackend interface { // If retrieving the latest [dockerImage] fails, the local image will be used. // Returns True is it was retrieved from cloud or False if it's a local image // Returns a string that represents the architecture of the image - FetchImage(ctx context.Context, image string, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) + FetchImage(ctx context.Context, image string, registrySpec *image_registry_spec.ImageRegistrySpec, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) PruneUnusedImages(ctx context.Context) ([]string, error) diff --git a/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go b/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go index 056048d9f9..128bb9473c 100644 --- a/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.26.1. DO NOT EDIT. +// Code generated by mockery v2.22.1. DO NOT EDIT. package backend_interface @@ -18,6 +18,8 @@ import ( image_download_mode "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_download_mode" + image_registry_spec "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + io "io" logs_aggregator "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/logs_aggregator" @@ -957,30 +959,30 @@ func (_c *MockKurtosisBackend_DumpKurtosis_Call) RunAndReturn(run func(context.C return _c } -// FetchImage provides a mock function with given fields: ctx, image, downloadMode -func (_m *MockKurtosisBackend) FetchImage(ctx context.Context, image string, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { - ret := _m.Called(ctx, image, downloadMode) +// FetchImage provides a mock function with given fields: ctx, image, registrySpec, downloadMode +func (_m *MockKurtosisBackend) FetchImage(ctx context.Context, image string, registrySpec *image_registry_spec.ImageRegistrySpec, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { + ret := _m.Called(ctx, image, registrySpec, downloadMode) var r0 bool var r1 string var r2 error - if rf, ok := ret.Get(0).(func(context.Context, string, image_download_mode.ImageDownloadMode) (bool, string, error)); ok { - return rf(ctx, image, downloadMode) + if rf, ok := ret.Get(0).(func(context.Context, string, *image_registry_spec.ImageRegistrySpec, image_download_mode.ImageDownloadMode) (bool, string, error)); ok { + return rf(ctx, image, registrySpec, downloadMode) } - if rf, ok := ret.Get(0).(func(context.Context, string, image_download_mode.ImageDownloadMode) bool); ok { - r0 = rf(ctx, image, downloadMode) + if rf, ok := ret.Get(0).(func(context.Context, string, *image_registry_spec.ImageRegistrySpec, image_download_mode.ImageDownloadMode) bool); ok { + r0 = rf(ctx, image, registrySpec, downloadMode) } else { r0 = ret.Get(0).(bool) } - if rf, ok := ret.Get(1).(func(context.Context, string, image_download_mode.ImageDownloadMode) string); ok { - r1 = rf(ctx, image, downloadMode) + if rf, ok := ret.Get(1).(func(context.Context, string, *image_registry_spec.ImageRegistrySpec, image_download_mode.ImageDownloadMode) string); ok { + r1 = rf(ctx, image, registrySpec, downloadMode) } else { r1 = ret.Get(1).(string) } - if rf, ok := ret.Get(2).(func(context.Context, string, image_download_mode.ImageDownloadMode) error); ok { - r2 = rf(ctx, image, downloadMode) + if rf, ok := ret.Get(2).(func(context.Context, string, *image_registry_spec.ImageRegistrySpec, image_download_mode.ImageDownloadMode) error); ok { + r2 = rf(ctx, image, registrySpec, downloadMode) } else { r2 = ret.Error(2) } @@ -996,14 +998,15 @@ type MockKurtosisBackend_FetchImage_Call struct { // FetchImage is a helper method to define mock.On call // - ctx context.Context // - image string +// - registrySpec *image_registry_spec.ImageRegistrySpec // - downloadMode image_download_mode.ImageDownloadMode -func (_e *MockKurtosisBackend_Expecter) FetchImage(ctx interface{}, image interface{}, downloadMode interface{}) *MockKurtosisBackend_FetchImage_Call { - return &MockKurtosisBackend_FetchImage_Call{Call: _e.mock.On("FetchImage", ctx, image, downloadMode)} +func (_e *MockKurtosisBackend_Expecter) FetchImage(ctx interface{}, image interface{}, registrySpec interface{}, downloadMode interface{}) *MockKurtosisBackend_FetchImage_Call { + return &MockKurtosisBackend_FetchImage_Call{Call: _e.mock.On("FetchImage", ctx, image, registrySpec, downloadMode)} } -func (_c *MockKurtosisBackend_FetchImage_Call) Run(run func(ctx context.Context, image string, downloadMode image_download_mode.ImageDownloadMode)) *MockKurtosisBackend_FetchImage_Call { +func (_c *MockKurtosisBackend_FetchImage_Call) Run(run func(ctx context.Context, image string, registrySpec *image_registry_spec.ImageRegistrySpec, downloadMode image_download_mode.ImageDownloadMode)) *MockKurtosisBackend_FetchImage_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(image_download_mode.ImageDownloadMode)) + run(args[0].(context.Context), args[1].(string), args[2].(*image_registry_spec.ImageRegistrySpec), args[3].(image_download_mode.ImageDownloadMode)) }) return _c } @@ -1013,7 +1016,7 @@ func (_c *MockKurtosisBackend_FetchImage_Call) Return(_a0 bool, _a1 string, _a2 return _c } -func (_c *MockKurtosisBackend_FetchImage_Call) RunAndReturn(run func(context.Context, string, image_download_mode.ImageDownloadMode) (bool, string, error)) *MockKurtosisBackend_FetchImage_Call { +func (_c *MockKurtosisBackend_FetchImage_Call) RunAndReturn(run func(context.Context, string, *image_registry_spec.ImageRegistrySpec, image_download_mode.ImageDownloadMode) (bool, string, error)) *MockKurtosisBackend_FetchImage_Call { _c.Call.Return(run) return _c } diff --git a/container-engine-lib/lib/backend_interface/objects/image_registry_spec/image_registry_spec.go b/container-engine-lib/lib/backend_interface/objects/image_registry_spec/image_registry_spec.go new file mode 100644 index 0000000000..aacd56e804 --- /dev/null +++ b/container-engine-lib/lib/backend_interface/objects/image_registry_spec/image_registry_spec.go @@ -0,0 +1,63 @@ +package image_registry_spec + +import ( + "encoding/json" + "github.com/kurtosis-tech/stacktrace" +) + +type ImageRegistrySpec struct { + // we do this way in order to have exported fields which can be marshalled + // and an unexported type for encapsulation + privateRegistrySpec *privateImageRegistrySpec +} + +type privateImageRegistrySpec struct { + Image string + Username string + Password string + RegistryAddr string +} + +func NewImageRegistrySpec(image, username, password, registryAddr string) *ImageRegistrySpec { + internalRegistrySpec := &privateImageRegistrySpec{ + Image: image, + Username: username, + Password: password, + RegistryAddr: registryAddr, + } + return &ImageRegistrySpec{privateRegistrySpec: internalRegistrySpec} +} + +func (irs *ImageRegistrySpec) GetImageName() string { + return irs.privateRegistrySpec.Image +} + +func (irs *ImageRegistrySpec) GetUsername() string { + return irs.privateRegistrySpec.Username +} + +func (irs *ImageRegistrySpec) GetPassword() string { + return irs.privateRegistrySpec.Password +} + +func (irs *ImageRegistrySpec) GetRegistryAddr() string { + return irs.privateRegistrySpec.RegistryAddr +} + +func (irs *ImageRegistrySpec) MarshalJSON() ([]byte, error) { + return json.Marshal(irs.privateRegistrySpec) +} + +func (irs *ImageRegistrySpec) UnmarshalJSON(data []byte) error { + + // Suppressing exhaustruct requirement because we want an object with zero values + // nolint: exhaustruct + unmarshalledPrivateStructPtr := &privateImageRegistrySpec{} + + if err := json.Unmarshal(data, unmarshalledPrivateStructPtr); err != nil { + return stacktrace.Propagate(err, "An error occurred unmarshalling the private struct") + } + + irs.privateRegistrySpec = unmarshalledPrivateStructPtr + return nil +} diff --git a/container-engine-lib/lib/backend_interface/objects/service/service_config.go b/container-engine-lib/lib/backend_interface/objects/service/service_config.go index 7952b76fcc..ff9f4d8bb4 100644 --- a/container-engine-lib/lib/backend_interface/objects/service/service_config.go +++ b/container-engine-lib/lib/backend_interface/objects/service/service_config.go @@ -3,6 +3,7 @@ package service import ( "encoding/json" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user" @@ -23,6 +24,11 @@ type privateServiceConfig struct { // If nil, container engine won't be able to build image for this service ImageBuildSpec *image_build_spec.ImageBuildSpec + // Configuration for container engine to pull an in a private registry behind authentication + // If nil, we will use the ContainerImageName and not use any auth + // Mutually exclusive from ImageBuildSpec, ContainerImageName + ImagerRegistrySpec *image_registry_spec.ImageRegistrySpec + PrivatePorts map[string]*port_spec.PortSpec PublicPorts map[string]*port_spec.PortSpec //TODO this is a huge hack to temporarily enable static ports for NEAR until we have a more productized solution @@ -56,6 +62,7 @@ type privateServiceConfig struct { func CreateServiceConfig( containerImageName string, imageBuildSpec *image_build_spec.ImageBuildSpec, + imageRegistrySpec *image_registry_spec.ImageRegistrySpec, privatePorts map[string]*port_spec.PortSpec, publicPorts map[string]*port_spec.PortSpec, entrypointArgs []string, @@ -79,6 +86,7 @@ func CreateServiceConfig( internalServiceConfig := &privateServiceConfig{ ContainerImageName: containerImageName, ImageBuildSpec: imageBuildSpec, + ImagerRegistrySpec: imageRegistrySpec, PrivatePorts: privatePorts, PublicPorts: publicPorts, EntrypointArgs: entrypointArgs, @@ -106,6 +114,10 @@ func (serviceConfig *ServiceConfig) GetImageBuildSpec() *image_build_spec.ImageB return serviceConfig.privateServiceConfig.ImageBuildSpec } +func (serviceConfig *ServiceConfig) GetImageRegistrySpec() *image_registry_spec.ImageRegistrySpec { + return serviceConfig.privateServiceConfig.ImagerRegistrySpec +} + func (serviceConfig *ServiceConfig) GetPrivatePorts() map[string]*port_spec.PortSpec { return serviceConfig.privateServiceConfig.PrivatePorts } diff --git a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go index 92fecd44c2..ad6087b4c9 100644 --- a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go +++ b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go @@ -3,8 +3,10 @@ package service import ( "encoding/json" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user" "github.com/stretchr/testify/require" "testing" "time" @@ -60,6 +62,7 @@ func getServiceConfigForTest(t *testing.T, imageName string) *ServiceConfig { serviceConfig, err := CreateServiceConfig( imageName, testImageBuildSpec(), + testImageRegistrySpec(), testPrivatePorts(t), testPublicPorts(t), []string{"bin", "bash", "ls"}, @@ -76,7 +79,7 @@ func getServiceConfigForTest(t *testing.T, imageName string) *ServiceConfig { "test-label-key": "test-label-value", "test-second-label-key": "test-second-label-value", }, - nil, + testServiceUser(), ) require.NoError(t, err) return serviceConfig @@ -174,3 +177,13 @@ func testImageBuildSpec() *image_build_spec.ImageBuildSpec { "path", "") } + +func testImageRegistrySpec() *image_registry_spec.ImageRegistrySpec { + return image_registry_spec.NewImageRegistrySpec("test-image", "test-userename", "test-password", "test-registry.io") +} + +func testServiceUser() *service_user.ServiceUser { + su := service_user.NewServiceUser(100) + su.SetGID(100) + return su +} diff --git a/container-engine-lib/lib/backend_interface/objects/service_user/service_user.go b/container-engine-lib/lib/backend_interface/objects/service_user/service_user.go index 521903046f..252d4fda67 100644 --- a/container-engine-lib/lib/backend_interface/objects/service_user/service_user.go +++ b/container-engine-lib/lib/backend_interface/objects/service_user/service_user.go @@ -1,7 +1,9 @@ package service_user import ( + "encoding/json" "fmt" + "github.com/kurtosis-tech/stacktrace" ) type UID int64 @@ -13,34 +15,61 @@ const ( ) type ServiceUser struct { - uid UID - gid GID - isGIDSet bool + privateServiceUser *privateServiceUser +} + +type privateServiceUser struct { + UID UID + GID GID + IsGIDSet bool } func NewServiceUser(uid UID) *ServiceUser { - return &ServiceUser{uid: uid, gid: defaultGidValue, isGIDSet: gidIsNotSet} + internalServiceUser := &privateServiceUser{ + UID: uid, + GID: defaultGidValue, + IsGIDSet: gidIsNotSet, + } + return &ServiceUser{privateServiceUser: internalServiceUser} } func (su *ServiceUser) GetUID() UID { - return su.uid + return su.privateServiceUser.UID } func (su *ServiceUser) GetGID() (GID, bool) { - if !su.isGIDSet { + if !su.privateServiceUser.IsGIDSet { return 0, false } - return su.gid, true + return su.privateServiceUser.GID, true } func (su *ServiceUser) SetGID(gid GID) { - su.gid = gid - su.isGIDSet = true + su.privateServiceUser.GID = gid + su.privateServiceUser.IsGIDSet = true } func (su *ServiceUser) GetUIDGIDPairAsStr() string { - if su.isGIDSet { - return fmt.Sprintf("%v:%v", su.uid, su.gid) + if su.privateServiceUser.IsGIDSet { + return fmt.Sprintf("%v:%v", su.privateServiceUser.UID, su.privateServiceUser.GID) } - return fmt.Sprintf("%v", su.uid) + return fmt.Sprintf("%v", su.privateServiceUser.UID) +} + +func (su ServiceUser) MarshalJSON() ([]byte, error) { + return json.Marshal(su.privateServiceUser) +} + +func (su *ServiceUser) UnmarshalJSON(data []byte) error { + + // Suppressing exhaustruct requirement because we want an object with zero values + // nolint: exhaustruct + unmarshalledPrivateStructPtr := &privateServiceUser{} + + if err := json.Unmarshal(data, unmarshalledPrivateStructPtr); err != nil { + return stacktrace.Propagate(err, "An error occurred unmarshalling the private struct") + } + + su.privateServiceUser = unmarshalledPrivateStructPtr + return nil } diff --git a/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go b/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go index 880425a762..6ba864fc65 100644 --- a/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go +++ b/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go @@ -305,6 +305,7 @@ func getServiceConfigForTest(t *testing.T, imageName string) *service.ServiceCon serviceConfig, err := service.CreateServiceConfig( imageName, nil, + nil, testPrivatePorts(t), testPublicPorts(t), []string{"bin", "bash", "ls"}, diff --git a/core/server/api_container/server/service_network/default_service_network_test.go b/core/server/api_container/server/service_network/default_service_network_test.go index 4cf738dee5..72311f04ba 100644 --- a/core/server/api_container/server/service_network/default_service_network_test.go +++ b/core/server/api_container/server/service_network/default_service_network_test.go @@ -1214,6 +1214,7 @@ func testServiceConfig(t *testing.T, imageName string) *service.ServiceConfig { nil, nil, nil, + nil, 0, 0, "", diff --git a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go index 9c8e728675..70eb4fb7e0 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go @@ -112,6 +112,7 @@ func KurtosisTypeConstructors() []*starlark.Builtin { starlark.NewBuiltin(service_config.ServiceConfigTypeName, service_config.NewServiceConfigType().CreateBuiltin()), starlark.NewBuiltin(service_config.ReadyConditionTypeName, service_config.NewReadyConditionType().CreateBuiltin()), starlark.NewBuiltin(service_config.ImageBuildSpecTypeName, service_config.NewImageBuildSpecType().CreateBuiltin()), + starlark.NewBuiltin(service_config.ImageRegistrySpecTypeName, service_config.NewImageRegistrySpec().CreateBuiltin()), starlark.NewBuiltin(service_config.UserTypeName, service_config.NewUserType().CreateBuiltin()), } } diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go index 1505bbce3b..93bea3a648 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go @@ -108,6 +108,8 @@ func validateSingleService(validatorEnvironment *startosis_validator.ValidatorEn if serviceConfig.GetImageBuildSpec() != nil { validatorEnvironment.AppendRequiredImageBuild(serviceConfig.GetContainerImageName(), serviceConfig.GetImageBuildSpec()) + } else if serviceConfig.GetImageRegistrySpec() != nil { + validatorEnvironment.AppendImageToPullWithAuth(serviceConfig.GetContainerImageName(), serviceConfig.GetImageRegistrySpec()) } else { validatorEnvironment.AppendRequiredImagePull(serviceConfig.GetContainerImageName()) } @@ -199,6 +201,7 @@ func replaceMagicStrings( renderedServiceConfig, err := service.CreateServiceConfig( serviceConfig.GetContainerImageName(), serviceConfig.GetImageBuildSpec(), + serviceConfig.GetImageRegistrySpec(), serviceConfig.GetPrivatePorts(), serviceConfig.GetPublicPorts(), entrypoints, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go index b5c39f0b11..c63af9e09d 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go @@ -41,6 +41,7 @@ func TestAddServiceShared_EntryPointArgsRuntimeValueAreReplaced(t *testing.T) { nil, nil, nil, + nil, []string{"-- " + runtimeValue}, nil, nil, @@ -85,6 +86,7 @@ func TestAddServiceShared_CmdArgsRuntimeValueAreReplaced(t *testing.T) { nil, nil, nil, + nil, []string{"bash", "-c", "sleep " + runtimeValue}, nil, nil, @@ -129,6 +131,7 @@ func TestAddServiceShared_EnvVarsWithRuntimeValueAreReplaced(t *testing.T) { nil, nil, nil, + nil, map[string]string{ "PORT": runtimeValue, }, @@ -180,6 +183,7 @@ func TestAddServiceShared_ServiceNameWithRuntimeValuesAreReplaced(t *testing.T) nil, nil, nil, + nil, 0, 0, "", diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go index 85dc263b06..e8e245cd2d 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go @@ -254,6 +254,7 @@ func getServiceConfig(image string, filesArtifactExpansion *service_directory.Fi nil, nil, nil, + nil, // This make sure that the container does not stop as soon as it starts // This only is needed for kubernetes at the moment // TODO: Instead of creating a service and running exec commands diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go index ffa912ae43..437ced821b 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go @@ -35,6 +35,7 @@ func (suite *KurtosisPlanInstructionTestSuite) TestAddService() { expectedServiceConfig, err := service.CreateServiceConfig( testContainerImageName, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go index 2f76d1eb83..7d8c097d7b 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go @@ -51,6 +51,7 @@ func (suite *KurtosisPlanInstructionTestSuite) TestAddServices() { expectedServiceConfig1, err := service.CreateServiceConfig( testContainerImageName, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, @@ -74,6 +75,7 @@ func (suite *KurtosisPlanInstructionTestSuite) TestAddServices() { expectedServiceConfig2, err := service.CreateServiceConfig( testContainerImageName, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go index ade8edebed..8d34280a93 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go @@ -70,6 +70,7 @@ func (t *serviceConfigImageBuildSpecTestCase) Assert(typeValue builtin_argument. expectedServiceConfig, err := service.CreateServiceConfig( testContainerImageName, expectedImageBuildSpec, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go new file mode 100644 index 0000000000..e881817fad --- /dev/null +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go @@ -0,0 +1,82 @@ +package test_engine + +import ( + "fmt" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/service_config" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" + "github.com/stretchr/testify/require" + "testing" +) + +type serviceConfigImageRegistrySpecTest struct { + *testing.T + serviceNetwork *service_network.MockServiceNetwork + packageContentProvider *startosis_packages.MockPackageContentProvider +} + +func (suite *KurtosisTypeConstructorTestSuite) TestServiceConfigWithImageRegistrySpec() { + suite.run(&serviceConfigImageRegistrySpecTest{ + T: suite.T(), + serviceNetwork: suite.serviceNetwork, + packageContentProvider: suite.packageContentProvider, + }) +} + +func (t *serviceConfigImageRegistrySpecTest) GetStarlarkCode() string { + imageRegistrySpec := fmt.Sprintf("%s(%s=%q, %s=%q, %s=%q, %s=%q)", + service_config.ImageRegistrySpecTypeName, + service_config.ImageAttr, + testContainerImageName, + service_config.RegistryAddrAttr, + testRegistryAddr, + service_config.RegistryUsernameAttr, + testRegistryUsername, + service_config.RegistryPasswordAttr, + testRegistryPassword, + ) + return fmt.Sprintf("%s(%s=%s)", + service_config.ServiceConfigTypeName, + service_config.ImageAttr, imageRegistrySpec) +} + +func (t *serviceConfigImageRegistrySpecTest) Assert(typeValue builtin_argument.KurtosisValueType) { + serviceConfigStarlark, ok := typeValue.(*service_config.ServiceConfig) + require.True(t, ok) + + serviceConfig, interpretationErr := serviceConfigStarlark.ToKurtosisType( + t.serviceNetwork, + testModuleMainFileLocator, + testModulePackageId, + t.packageContentProvider, + testNoPackageReplaceOptions) + require.Nil(t, interpretationErr) + + expectedImageRegistrySpec := image_registry_spec.NewImageRegistrySpec(testContainerImageName, testRegistryUsername, testRegistryPassword, testRegistryAddr) + expectedServiceConfig, err := service.CreateServiceConfig( + testContainerImageName, + nil, + expectedImageRegistrySpec, + map[string]*port_spec.PortSpec{}, + map[string]*port_spec.PortSpec{}, + nil, + nil, + map[string]string{}, + nil, + nil, + 0, + 0, + service_config.DefaultPrivateIPAddrPlaceholder, + 0, + 0, + map[string]string{}, + nil, + ) + require.NoError(t, err) + require.Equal(t, expectedServiceConfig, serviceConfig) + require.Equal(t, expectedImageRegistrySpec, serviceConfig.GetImageRegistrySpec()) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go index e48ea20984..cedd4374dc 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go @@ -47,6 +47,7 @@ func (t *serviceConfigMinimalTestCase) Assert(typeValue builtin_argument.Kurtosi expectedServiceConfig, err := service.CreateServiceConfig( testContainerImageName, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go index d869efb1b6..c32daa7cba 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go @@ -34,6 +34,10 @@ var ( testOnDiskContextDirPath = "kurtosis-data/test-package" testOnDiskContainerImagePath = "kurtosis-data/test-package/Dockerfile" + testRegistryAddr = "http://registry.test.io" + testRegistryUsername = "kurtosis" + testRegistryPassword = "password" + testPrivatePortId = "grpc" testPrivatePortNumber = uint16(1323) testPrivatePortProtocolStr = "TCP" diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_registry_spec.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_registry_spec.go new file mode 100644 index 0000000000..3957006226 --- /dev/null +++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_registry_spec.go @@ -0,0 +1,167 @@ +package service_config + +import ( + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_type_constructor" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors" + "go.starlark.net/starlark" +) + +const ( + ImageRegistrySpecTypeName = "ImageRegistrySpec" + + RegistryImageAttr = "image" + RegistryAddrAttr = "registry" + RegistryUsernameAttr = "username" + RegistryPasswordAttr = "password" +) + +func NewImageRegistrySpec() *kurtosis_type_constructor.KurtosisTypeConstructor { + return &kurtosis_type_constructor.KurtosisTypeConstructor{ + KurtosisBaseBuiltin: &kurtosis_starlark_framework.KurtosisBaseBuiltin{ + Name: ImageRegistrySpecTypeName, + Arguments: []*builtin_argument.BuiltinArgument{ + { + Name: RegistryImageAttr, + IsOptional: false, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.NonEmptyString(value, RegistryImageAttr) + }, + }, + { + Name: RegistryAddrAttr, + IsOptional: false, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.NonEmptyString(value, RegistryAddrAttr) + }, + }, + { + Name: RegistryUsernameAttr, + IsOptional: false, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.NonEmptyString(value, RegistryUsernameAttr) + }, + }, + { + Name: RegistryPasswordAttr, + IsOptional: false, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.NonEmptyString(value, RegistryUsernameAttr) + }, + }, + }, + }, + Instantiate: instantiateImageRegistrySpec, + } +} + +func instantiateImageRegistrySpec(arguments *builtin_argument.ArgumentValuesSet) (builtin_argument.KurtosisValueType, *startosis_errors.InterpretationError) { + kurtosisValueType, err := kurtosis_type_constructor.CreateKurtosisStarlarkTypeDefault(ImageRegistrySpecTypeName, arguments) + if err != nil { + return nil, err + } + return &ImageRegistrySpec{ + KurtosisValueTypeDefault: kurtosisValueType, + }, nil +} + +// ImageRegistrySpec is a starlark.Value that holds all the information to log in to a Docker registry +type ImageRegistrySpec struct { + *kurtosis_type_constructor.KurtosisValueTypeDefault +} + +func (irs *ImageRegistrySpec) Copy() (builtin_argument.KurtosisValueType, error) { + copiedValueType, err := irs.KurtosisValueTypeDefault.Copy() + if err != nil { + return nil, err + } + return &ImageRegistrySpec{ + KurtosisValueTypeDefault: copiedValueType, + }, nil +} + +// GetImage returns the image that needs to be pulled +func (irs *ImageRegistrySpec) GetImage() (string, *startosis_errors.InterpretationError) { + image, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, RegistryImageAttr) + if interpretationErr != nil { + return "", interpretationErr + } + if !found { + return "", startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", + RegistryImageAttr, ImageRegistrySpecTypeName) + } + imageStr := image.GoString() + return imageStr, nil +} + +// GetPassword returns the password of the account for the registry +func (irs *ImageRegistrySpec) GetPassword() (string, *startosis_errors.InterpretationError) { + password, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, RegistryPasswordAttr) + if interpretationErr != nil { + return "", interpretationErr + } + if !found { + return "", startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", + RegistryPasswordAttr, ImageRegistrySpecTypeName) + } + passwordStr := password.GoString() + return passwordStr, nil +} + +// GetRegistryAddr returns the address of the registry from which the image has to be pulled +func (irs *ImageRegistrySpec) GetRegistryAddr() (string, *startosis_errors.InterpretationError) { + registryAddr, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, RegistryAddrAttr) + if interpretationErr != nil { + return "", interpretationErr + } + if !found { + return "", startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", + RegistryAddrAttr, ImageRegistrySpecTypeName) + } + registryAddrStr := registryAddr.GoString() + return registryAddrStr, nil +} + +// GetUsername returns the address of the registry from which the image has to be pulled +func (irs *ImageRegistrySpec) GetUsername() (string, *startosis_errors.InterpretationError) { + registryAddr, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, RegistryUsernameAttr) + if interpretationErr != nil { + return "", interpretationErr + } + if !found { + return "", startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", + RegistryUsernameAttr, ImageRegistrySpecTypeName) + } + registryAddrStr := registryAddr.GoString() + return registryAddrStr, nil +} + +func (irs *ImageRegistrySpec) ToKurtosisType() (*image_registry_spec.ImageRegistrySpec, *startosis_errors.InterpretationError) { + image, err := irs.GetImage() + if err != nil { + return nil, err + } + + username, err := irs.GetUsername() + if err != nil { + return nil, err + } + + password, err := irs.GetPassword() + if err != nil { + return nil, err + } + + registryAddr, err := irs.GetRegistryAddr() + if err != nil { + return nil, err + } + + return image_registry_spec.NewImageRegistrySpec(image, username, password, registryAddr), nil +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go index df8511012e..1b12a06547 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go @@ -3,6 +3,7 @@ package service_config import ( "fmt" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" @@ -231,6 +232,7 @@ func (config *ServiceConfig) ToKurtosisType( var imageName string var maybeImageBuildSpec *image_build_spec.ImageBuildSpec + var maybeImageRegistrySpec *image_registry_spec.ImageRegistrySpec rawImageAttrValue, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.Value](config.KurtosisValueTypeDefault, ImageAttr) if interpretationErr != nil { return nil, interpretationErr @@ -238,7 +240,7 @@ func (config *ServiceConfig) ToKurtosisType( if !found { return nil, startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", ImageAttr, ServiceConfigTypeName) } - imageName, maybeImageBuildSpec, interpretationErr = convertImage( + imageName, maybeImageBuildSpec, maybeImageRegistrySpec, interpretationErr = convertImage( rawImageAttrValue, locatorOfModuleInWhichThisBuiltInIsBeingCalled, packageId, @@ -455,6 +457,7 @@ func (config *ServiceConfig) ToKurtosisType( serviceConfig, err := service.CreateServiceConfig( imageName, maybeImageBuildSpec, + maybeImageRegistrySpec, privatePorts, publicPorts, entryPointArgs, @@ -618,28 +621,36 @@ func convertFilesArguments(attrNameForLogging string, filesDict *starlark.Dict) // If [image] is an ImageBuildSpec type, returns name for the image to build and ImageBuildSpec converted to KurtosisType // If [image] is a string, returns the image name with no image build spec (image will be fetched from local cache or remote) +// If [image] is an ImageRegistrySpec type, returns the name for the image and the ImageRegistrySpec converted to KurtosisType func convertImage( image starlark.Value, locatorOfModuleInWhichThisBuiltInIsBeingCalled string, packageId string, packageContentProvider startosis_packages.PackageContentProvider, - packageReplaceOptions map[string]string) (string, *image_build_spec.ImageBuildSpec, *startosis_errors.InterpretationError) { + packageReplaceOptions map[string]string) (string, *image_build_spec.ImageBuildSpec, *image_registry_spec.ImageRegistrySpec, *startosis_errors.InterpretationError) { imageBuildSpecStarlarkType, isImageBuildSpecStarlarkType := image.(*ImageBuildSpec) + imageRegistrySpecStarlarkType, isImageRegistrySpecStarlarkType := image.(*ImageRegistrySpec) if isImageBuildSpecStarlarkType { imageBuildSpec, interpretationErr := imageBuildSpecStarlarkType.ToKurtosisType(locatorOfModuleInWhichThisBuiltInIsBeingCalled, packageId, packageContentProvider, packageReplaceOptions) if interpretationErr != nil { - return "", nil, interpretationErr + return "", nil, nil, interpretationErr } imageName, interpretationErr := imageBuildSpecStarlarkType.GetImageName() if interpretationErr != nil { - return "", nil, interpretationErr + return "", nil, nil, interpretationErr } - return imageName, imageBuildSpec, nil + return imageName, imageBuildSpec, nil, nil + } else if isImageRegistrySpecStarlarkType { + imageRegistrySpec, interpretationErr := imageRegistrySpecStarlarkType.ToKurtosisType() + if interpretationErr != nil { + return "", nil, nil, interpretationErr + } + return imageRegistrySpec.GetImageName(), nil, imageRegistrySpec, nil } else { imageName, interpretationErr := kurtosis_types.SafeCastToString(image, ImageAttr) if interpretationErr != nil { - return "", nil, interpretationErr + return "", nil, nil, interpretationErr } - return imageName, nil, nil + return imageName, nil, nil, nil } } diff --git a/core/server/api_container/server/startosis_engine/startosis_validator/images_validator.go b/core/server/api_container/server/startosis_engine/startosis_validator/images_validator.go index 93c23432e4..8cc701b9aa 100644 --- a/core/server/api_container/server/startosis_engine/startosis_validator/images_validator.go +++ b/core/server/api_container/server/startosis_engine/startosis_validator/images_validator.go @@ -3,6 +3,7 @@ package startosis_validator import ( "context" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "sync" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface" @@ -49,9 +50,9 @@ func (validator *ImagesValidator) Validate( }() wg := &sync.WaitGroup{} - for imageName := range environment.imagesToPull { + for imageName, maybeImageRegistrySpec := range environment.imagesToPull { wg.Add(1) - go fetchImageFromBackend(ctx, wg, imageCurrentlyValidating, validator.kurtosisBackend, imageName, environment.imageDownloadMode, imageValidationErrors, imageValidationStarted, imageValidationFinished) + go fetchImageFromBackend(ctx, wg, imageCurrentlyValidating, validator.kurtosisBackend, imageName, maybeImageRegistrySpec, environment.imageDownloadMode, imageValidationErrors, imageValidationStarted, imageValidationFinished) } for imageName, imageBuildSpec := range environment.imagesToBuild { wg.Add(1) @@ -61,7 +62,7 @@ func (validator *ImagesValidator) Validate( logrus.Debug("All image validation submitted, currently in progress.") } -func fetchImageFromBackend(ctx context.Context, wg *sync.WaitGroup, imageCurrentlyDownloading chan bool, backend *backend_interface.KurtosisBackend, imageName string, imageDownloadMode image_download_mode.ImageDownloadMode, pullErrors chan<- error, imageDownloadStarted chan<- string, imageDownloadFinished chan<- *ValidatedImage) { +func fetchImageFromBackend(ctx context.Context, wg *sync.WaitGroup, imageCurrentlyDownloading chan bool, backend *backend_interface.KurtosisBackend, imageName string, registrySpec *image_registry_spec.ImageRegistrySpec, imageDownloadMode image_download_mode.ImageDownloadMode, pullErrors chan<- error, imageDownloadStarted chan<- string, imageDownloadFinished chan<- *ValidatedImage) { logrus.Debugf("Requesting the download of image: '%s'", imageName) var imagePulledFromRemote bool var imageArch string @@ -75,7 +76,7 @@ func fetchImageFromBackend(ctx context.Context, wg *sync.WaitGroup, imageCurrent }() logrus.Debugf("Starting the download of image: '%s'", imageName) - imagePulledFromRemote, imageArch, err := (*backend).FetchImage(ctx, imageName, imageDownloadMode) + imagePulledFromRemote, imageArch, err := (*backend).FetchImage(ctx, imageName, registrySpec, imageDownloadMode) if err != nil { logrus.Warnf("Container image '%s' download failed. Error was: '%s'", imageName, err.Error()) pullErrors <- startosis_errors.WrapWithValidationError(err, "Failed fetching the required image '%v'.", imageName) diff --git a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go index dcea4e0338..bb5b05c05b 100644 --- a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go +++ b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go @@ -4,6 +4,7 @@ import ( "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/compute_resources" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_download_mode" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors" @@ -12,7 +13,7 @@ import ( // ValidatorEnvironment fields are not exported so that only validators can access its fields type ValidatorEnvironment struct { - imagesToPull map[string]bool // "set" of images that need to be downloaded + imagesToPull map[string]*image_registry_spec.ImageRegistrySpec // "set" of images that need to be downloaded imagesToBuild map[string]*image_build_spec.ImageBuildSpec serviceNames map[service.ServiceName]ComponentExistence artifactNames map[string]ComponentExistence @@ -36,7 +37,7 @@ func NewValidatorEnvironment(serviceNames map[service.ServiceName]bool, artifact artifactNamesWithComponentExistence[artifactName] = ComponentExistedBeforePackageRun } return &ValidatorEnvironment{ - imagesToPull: map[string]bool{}, + imagesToPull: map[string]*image_registry_spec.ImageRegistrySpec{}, imagesToBuild: map[string]*image_build_spec.ImageBuildSpec{}, serviceNames: serviceNamesWithComponentExistence, artifactNames: artifactNamesWithComponentExistence, @@ -53,13 +54,17 @@ func NewValidatorEnvironment(serviceNames map[service.ServiceName]bool, artifact } func (environment *ValidatorEnvironment) AppendRequiredImagePull(containerImage string) { - environment.imagesToPull[containerImage] = true + environment.imagesToPull[containerImage] = nil } func (environment *ValidatorEnvironment) AppendRequiredImageBuild(containerImage string, imageBuildSpec *image_build_spec.ImageBuildSpec) { environment.imagesToBuild[containerImage] = imageBuildSpec } +func (environmemt *ValidatorEnvironment) AppendImageToPullWithAuth(containerImage string, registrySpec *image_registry_spec.ImageRegistrySpec) { + environmemt.imagesToPull[containerImage] = registrySpec +} + func (environment *ValidatorEnvironment) GetNumberOfContainerImagesToProcess() uint32 { return uint32(len(environment.imagesToPull) + len(environment.imagesToBuild)) } diff --git a/docs/docs/api-reference/starlark-reference/service-config.md b/docs/docs/api-reference/starlark-reference/service-config.md index 7f8158165f..ab54be696e 100644 --- a/docs/docs/api-reference/starlark-reference/service-config.md +++ b/docs/docs/api-reference/starlark-reference/service-config.md @@ -12,6 +12,9 @@ config = ServiceConfig( # the underlying container engine. # If a string is provided, Kurtosis will by default detect if images exists locally, or pull from container registry if not. # If an ImageBuildSpec is provided, Kurtosis will build the image. + # If an ImageRegistrySpec is provided, Kurtosis will pull the image from the given registry with the given credentials + # Note for now ImageRegistrySpec is Docker only and the authentication gets ignored on Kubernetes + # Reach out to the team if you want to run Kurtosis with private images on Kubernetes # MANDATORY image = "kurtosistech/example-datastore-server", @@ -32,6 +35,24 @@ config = ServiceConfig( target_stage="" ) + OR + + image = ImageRegistrySpec( + # The name of the image that needs to be pulled qualified with the registry + # MANDATORY + name = "my.registry.io/my-user/my-image", + + # The username that will be used to pull the image from the given registry + # MANDATORY + usernamee = "my-user", + + # The password that will be used to pull the image from the given registry + password = "password", + + # The URL of the registry + registry = "http://my.registry.io/" + ) + # The ports that the container should listen on, identified by a user-friendly ID that can be used to select the port again in the future. # If no ports are provided, no ports will be exposed on the host machine, unless there is an EXPOSE in the Dockerfile # OPTIONAL (Default: {}) From 213e9b6484b2e48492ecb0aa54ec485e6ddfc20d Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:16:51 -0300 Subject: [PATCH 002/102] chore(main): release 0.86.9 (#2064) :robot: I have created a release *beep* *boop* --- ## [0.86.9](https://github.com/kurtosis-tech/kurtosis/compare/0.86.8...0.86.9) (2024-01-22) ### Features * added support for private registries on docker ([#2058](https://github.com/kurtosis-tech/kurtosis/issues/2058)) ([7cda3d0](https://github.com/kurtosis-tech/kurtosis/commit/7cda3d08e4867352cca5c52f7766e04daa73d029)) * emui strong indicator for form optional/required ([#2062](https://github.com/kurtosis-tech/kurtosis/issues/2062)) ([f0f51b4](https://github.com/kurtosis-tech/kurtosis/commit/f0f51b416221e30c489a2540057af0ba912f1cdf)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 8 ++++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- api/typescript/src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 19 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 206f931075..aa13fe9037 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.86.9](https://github.com/kurtosis-tech/kurtosis/compare/0.86.8...0.86.9) (2024-01-22) + + +### Features + +* added support for private registries on docker ([#2058](https://github.com/kurtosis-tech/kurtosis/issues/2058)) ([7cda3d0](https://github.com/kurtosis-tech/kurtosis/commit/7cda3d08e4867352cca5c52f7766e04daa73d029)) +* emui strong indicator for form optional/required ([#2062](https://github.com/kurtosis-tech/kurtosis/issues/2062)) ([f0f51b4](https://github.com/kurtosis-tech/kurtosis/commit/f0f51b416221e30c489a2540057af0ba912f1cdf)) + ## [0.86.8](https://github.com/kurtosis-tech/kurtosis/compare/0.86.7...0.86.8) (2024-01-18) diff --git a/LICENSE.md b/LICENSE.md index 4c4d774f4f..0612c6370b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.8 +Licensed Work: Kurtosis 0.86.9 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-01-18 +Change Date: 2028-01-22 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index dd9d5868f8..3d5e4dacd5 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.8" + KurtosisVersion = "0.86.9" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index d08175c026..e9b5194dd1 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.8" +version = "0.86.9" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index 558bc39ca3..cc06966592 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.8", + "version": "0.86.9", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index bd8965f8dc..434ffff97c 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.8" +export const KURTOSIS_VERSION: string = "0.86.9" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 39898c6c60..c3f8bf704a 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.8", + "version": "0.86.9", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index fe7a01d4e3..1eac4ebd92 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.8", + "version": "0.86.9", "private": true, "homepage": ".", "dependencies": { @@ -9,7 +9,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.8", + "kurtosis-ui-components": "0.86.9", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "yaml": "^2.3.4" diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index 43c66376d0..77aa8f95b7 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.8", + "version": "0.86.9", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 1edadd1006..e7a090ce81 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.8 +0.86.9 From 8a0241f0a12a68628d8a2f5da8f4bd78acc95afa Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Mon, 22 Jan 2024 16:46:17 +0000 Subject: [PATCH 003/102] docs: fix typo (#2067) --- docs/docs/api-reference/starlark-reference/service-config.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/api-reference/starlark-reference/service-config.md b/docs/docs/api-reference/starlark-reference/service-config.md index ab54be696e..5d4a5534e5 100644 --- a/docs/docs/api-reference/starlark-reference/service-config.md +++ b/docs/docs/api-reference/starlark-reference/service-config.md @@ -44,7 +44,7 @@ config = ServiceConfig( # The username that will be used to pull the image from the given registry # MANDATORY - usernamee = "my-user", + username = "my-user", # The password that will be used to pull the image from the given registry password = "password", @@ -216,4 +216,4 @@ The `user` field expects a `User`[user] object being passed. [ready-condition]: ./ready-condition.md [locators]: ../../advanced-concepts/locators.md [package]: ../../advanced-concepts/packages.md -[user]: ./user.md \ No newline at end of file +[user]: ./user.md From d3897630e34c3cf906cf0a1a00c945c86a953eba Mon Sep 17 00:00:00 2001 From: Ben Gazzard Date: Mon, 22 Jan 2024 18:14:10 +0000 Subject: [PATCH 004/102] chore: Increase the cypress default command timeout to 20s (#2068) ## Description: This PR increases the default command timeout in cypress in an attempt to address flakey tests reported by @h4ck3rk3y on slack. Examples of flakey runs: * https://app.circleci.com/pipelines/github/kurtosis-tech/kurtosis/10765/workflows/2d6e411a-d6b9-472b-9495-1ea69b304402/jobs/154148 * https://app.circleci.com/pipelines/github/kurtosis-tech/kurtosis/10773/workflows/4a2b122f-bdda-48f1-b4f3-df29ae021949/jobs/154294 * https://app.circleci.com/pipelines/github/kurtosis-tech/kurtosis/10755/workflows/f9e01095-678e-498c-94b9-ce47bfe771bd/jobs/154037 * https://app.circleci.com/pipelines/github/kurtosis-tech/kurtosis/10751/workflows/d9757f39-b633-4996-81a5-462cb21ebc5c/jobs/153944 Looking at these it seems another common thread is that the enclave list test is flakey at an unknown point - specifically some promise exception can be thrown. I think this is probably in the log viewer - and not actually indicative of the test failing. For this reason I've disabled the unhandled promise exception failures in this pr. ## Is this change user facing? NO ## References (if applicable): * https://docs.cypress.io/guides/core-concepts/retry-ability#Increase-time-to-retry * https://docs.cypress.io/guides/references/configuration#Overriding-Options --- enclave-manager/web/cypress.config.ts | 1 + enclave-manager/web/cypress/e2e/enclaveList.cy.ts | 2 +- enclave-manager/web/cypress/support/e2e.ts | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/enclave-manager/web/cypress.config.ts b/enclave-manager/web/cypress.config.ts index 17161e32e0..c0d7712df5 100644 --- a/enclave-manager/web/cypress.config.ts +++ b/enclave-manager/web/cypress.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from "cypress"; export default defineConfig({ e2e: { + defaultCommandTimeout: 20000, setupNodeEvents(on, config) { // implement node event listeners here }, diff --git a/enclave-manager/web/cypress/e2e/enclaveList.cy.ts b/enclave-manager/web/cypress/e2e/enclaveList.cy.ts index 998e4a16ee..a8530221f9 100644 --- a/enclave-manager/web/cypress/e2e/enclaveList.cy.ts +++ b/enclave-manager/web/cypress/e2e/enclaveList.cy.ts @@ -23,7 +23,7 @@ describe("Enclave List", () => { cy.contains("button", "Edit").click(); cy.focusInputWithLabel("Max CPU").type("1024"); cy.contains("button", "Update").click(); - cy.contains("Script completed", { timeout: 10 * 1000 }); + cy.contains("Script completed", { timeout: 30 * 1000 }); }); it("Shows a new enclave in the list", () => { diff --git a/enclave-manager/web/cypress/support/e2e.ts b/enclave-manager/web/cypress/support/e2e.ts index 6a173d6fcb..f65a76d6e8 100644 --- a/enclave-manager/web/cypress/support/e2e.ts +++ b/enclave-manager/web/cypress/support/e2e.ts @@ -18,3 +18,11 @@ import "./commands"; // Alternatively you can use CommonJS syntax: // require('./commands') + +Cypress.on("uncaught:exception", (err, runnable, promise) => { + // Log streaming seems to occasionally close unexpectedly, causing a promise exception to be thrown. + // Because of this we shouldn't fail tests when this happens. + if (promise) { + return false; + } +}); From 3cf2f596bc559df667d6ce680f4233b542473169 Mon Sep 17 00:00:00 2001 From: Ben Gazzard Date: Wed, 24 Jan 2024 13:42:04 +0000 Subject: [PATCH 005/102] feat: emui centered layout (#2070) ## Description: This PR implements main app centering, whilst maintaining `maxWidth: 1440px` per the mocks. [Screencast from 22-01-24 17:25:05.webm](https://github.com/kurtosis-tech/kurtosis/assets/4419574/dcc3fe9d-3c63-4116-be56-8f66605b822f) ## Is this change user facing? YES ## References (if applicable): * Fixes #2061 --- .../web/packages/components/src/AppLayout.tsx | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/enclave-manager/web/packages/components/src/AppLayout.tsx b/enclave-manager/web/packages/components/src/AppLayout.tsx index 6959a3a4d0..dff803a5a5 100644 --- a/enclave-manager/web/packages/components/src/AppLayout.tsx +++ b/enclave-manager/web/packages/components/src/AppLayout.tsx @@ -42,7 +42,13 @@ export const AppPageLayout = ({ preventPageScroll, children }: AppPageLayoutProp if (numberOfChildren === 1) { return ( - + - + ); } @@ -76,7 +82,7 @@ export const AppPageLayout = ({ preventPageScroll, children }: AppPageLayoutProp if (numberOfChildren === 2 && Array.isArray(children)) { return ( - + {children[0]} - - - {children[1]} + + + + {children[1]} + ); From 9d9292b11307d9c1586f13bb2deeb1d306167083 Mon Sep 17 00:00:00 2001 From: leoporoli Date: Wed, 24 Jan 2024 11:02:19 -0300 Subject: [PATCH 006/102] fix: remove log printing env vars (#2069) ## Description: remove log printing env vars ## Is this change user facing? NO ## References (if applicable): --- .../metrics_reporting/metrics_reporting_kurtosis_backend.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go index 27fed58069..3adc276992 100644 --- a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go @@ -213,11 +213,11 @@ func (backend *MetricsReportingKurtosisBackend) CreateAPIContainer( customEnvVars, ) if err != nil { + // WARNING: remember not to print 'customEnvVars' because it could end up creating a secret info leak return nil, stacktrace.Propagate( err, - "An error occurred creating an API container from image '%v' with envvars: %+v", + "An error occurred creating an API container from image '%v'", image, - customEnvVars, ) } return result, nil From d54690509cfb03fce5c7b375c3f5f74e4b994eb2 Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:54:39 -0300 Subject: [PATCH 007/102] chore(main): release 0.86.10 (#2072) :robot: I have created a release *beep* *boop* --- ## [0.86.10](https://github.com/kurtosis-tech/kurtosis/compare/0.86.9...0.86.10) (2024-01-24) ### Features * emui centered layout ([#2070](https://github.com/kurtosis-tech/kurtosis/issues/2070)) ([3cf2f59](https://github.com/kurtosis-tech/kurtosis/commit/3cf2f596bc559df667d6ce680f4233b542473169)) ### Bug Fixes * remove log printing env vars ([#2069](https://github.com/kurtosis-tech/kurtosis/issues/2069)) ([9d9292b](https://github.com/kurtosis-tech/kurtosis/commit/9d9292b11307d9c1586f13bb2deeb1d306167083)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 12 ++++++++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- .../src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 23 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa13fe9037..4df0e30a2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [0.86.10](https://github.com/kurtosis-tech/kurtosis/compare/0.86.9...0.86.10) (2024-01-24) + + +### Features + +* emui centered layout ([#2070](https://github.com/kurtosis-tech/kurtosis/issues/2070)) ([3cf2f59](https://github.com/kurtosis-tech/kurtosis/commit/3cf2f596bc559df667d6ce680f4233b542473169)) + + +### Bug Fixes + +* remove log printing env vars ([#2069](https://github.com/kurtosis-tech/kurtosis/issues/2069)) ([9d9292b](https://github.com/kurtosis-tech/kurtosis/commit/9d9292b11307d9c1586f13bb2deeb1d306167083)) + ## [0.86.9](https://github.com/kurtosis-tech/kurtosis/compare/0.86.8...0.86.9) (2024-01-22) diff --git a/LICENSE.md b/LICENSE.md index 0612c6370b..a891d65686 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.9 +Licensed Work: Kurtosis 0.86.10 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-01-22 +Change Date: 2028-01-24 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 3d5e4dacd5..256810054b 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.9" + KurtosisVersion = "0.86.10" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index e9b5194dd1..ef8e4375fe 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.9" +version = "0.86.10" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index cc06966592..a174485949 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.9", + "version": "0.86.10", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index 434ffff97c..eb1aebc96e 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.9" +export const KURTOSIS_VERSION: string = "0.86.10" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index c3f8bf704a..547554ca88 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.9", + "version": "0.86.10", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index 1eac4ebd92..6969b3d830 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.9", + "version": "0.86.10", "private": true, "homepage": ".", "dependencies": { @@ -9,7 +9,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.9", + "kurtosis-ui-components": "0.86.10", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "yaml": "^2.3.4" diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index 77aa8f95b7..14dcd5d42e 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.9", + "version": "0.86.10", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index e7a090ce81..9c2586e677 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.9 +0.86.10 From a35e0a2cee4bb771ccf095271d4b8bb08cc0bcf4 Mon Sep 17 00:00:00 2001 From: leoporoli Date: Wed, 24 Jan 2024 18:06:55 -0300 Subject: [PATCH 008/102] feat: adding error logs when K8s resource creation fails in the create enclave workflow (#2074) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: adding error logs when K8s resource creation fails in the create enclave workflow ## Is this change user facing? NO ## References (if applicable): This was motivated during user support, we received this engine's logs that show us something fails when creating the APIC's pod but we can't see the error because something failed later when removing the other k8s resources. These new log lines should show us the error in the APIC's pod creation in this scenario Logs: ``` 2024-01-24T19:03:09.828984663Z DEBU[2024-01-24T19:03:09Z][kubernetes_manager.go:CreatePod] Going to start pod using the following JSON: {"metadata":{"name":"kurtosis-api","creationTimestamp":null,"labels":{"kurtosistech.com/app-id":"kurtosis","kurtosistech.com/enclave-id":"a1367260310749f9a9406218a09fd9ae","kurtosistech.com/resource-type":"api-container"}},"spec":{"volumes":[{"name":"enclave-data","persistentVolumeClaim":{"claimName":"enclave-data"}}],"containers":[{"name":"kurtosis-core-api","image":"kurtosistech/core:0.86.8","ports":[{"name":"grpc","containerPort":7443,"protocol":"TCP"}],"env":[{"name":"OWN_IP_ADDRESS","value":"10.98.123.85"},{"name":"SERIALIZED_ARGS","value":"{\"version\":\"0.86.8\",\"logLevel\":\"debug\",\"grpcListenPortNum\":7443,\"enclaveUuid\":\"a1367260310749f9a9406218a09fd9ae\",\"enclaveDataVolume\":\"/kurtosis-data\",\"kurtosisBackendType\":\"kubernetes\",\"kurtosisBackendConfig\":{\"StorageClass\":\"vultr-block-storage-hdd\"},\"enclaveEnvVars\":\"{}\",\"isProductionEnclave\":false,\"metricsUserID\":\"3dd3097f9bb3c18f44b93677edc2b20a3deee4368817088b715b50d70398fa04\",\"didUserAcceptSendingMetrics\":false,\"is_ci\":false,\"cloud_user_id\":\"\",\"cloud_instance_id\":\"\"}"},{"name":"API_CONTAINER_OWN_NAMESPACE_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}}],"resources":{},"volumeMounts":[{"name":"enclave-data","mountPath":"/kurtosis-data"}]}],"restartPolicy":"OnFailure","serviceAccountName":"kurtosis-api"},"status":{}} 2024-01-24T19:04:12.551574597Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func6] Creating the API container didn't complete successfully, so we tried to delete role binding 'kurtosis-api' in namespace 'kt-rusty-forest' that we created but an error was thrown: 2024-01-24T19:04:12.551599695Z Failed to delete role bindings with name 'kurtosis-api' in namespace 'kt-rusty-forest' 2024-01-24T19:04:12.551606898Z --- at /home/circleci/project/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go:856 (KubernetesManager.RemoveRoleBindings) --- 2024-01-24T19:04:12.551613080Z Caused by: client rate limiter Wait returned an error: context canceled 2024-01-24T19:04:12.551619231Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func6] ACTION REQUIRED: You'll need to manually remove role binding with name 'kurtosis-api'!!!!!!! 2024-01-24T19:04:12.551773996Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func5] Creating the API container didn't complete successfully, so we tried to delete role 'kurtosis-api' in namespace 'kt-rusty-forest' that we created but an error was thrown: 2024-01-24T19:04:12.551803382Z Failed to delete role with name 'kurtosis-api' in namespace 'kt-rusty-forest' 2024-01-24T19:04:12.551809623Z --- at /home/circleci/project/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go:767 (KubernetesManager.RemoveRole) --- 2024-01-24T19:04:12.551847205Z Caused by: client rate limiter Wait returned an error: context canceled 2024-01-24T19:04:12.551953247Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func5] ACTION REQUIRED: You'll need to manually remove role with name 'kurtosis-api'!!!!!!! 2024-01-24T19:04:12.552051153Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func4] Creating the API container didn't complete successfully, so we tried to delete cluster role binding 'kurtosis-api-a1367260310749f9a9406218a09fd9ae' that we created but an error was thrown: 2024-01-24T19:04:12.552056072Z Failed to delete cluster role binding with name 'kurtosis-api-a1367260310749f9a9406218a09fd9ae' 2024-01-24T19:04:12.552060330Z --- at /home/circleci/project/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go:1032 (KubernetesManager.RemoveClusterRoleBindings) --- 2024-01-24T19:04:12.552063136Z Caused by: client rate limiter Wait returned an error: context canceled 2024-01-24T19:04:12.552087923Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func4] ACTION REQUIRED: You'll need to manually remove cluster role binding with name 'kurtosis-api-a1367260310749f9a9406218a09fd9ae'!!!!!!! 2024-01-24T19:04:12.552206007Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func3] Creating the API container didn't complete successfully, so we tried to delete cluster role 'kurtosis-api-a1367260310749f9a9406218a09fd9ae' that we created but an error was thrown: 2024-01-24T19:04:12.552211899Z Failed to delete cluster role with name 'kurtosis-api-a1367260310749f9a9406218a09fd9ae' 2024-01-24T19:04:12.552214745Z --- at /home/circleci/project/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go:944 (KubernetesManager.RemoveClusterRole) --- 2024-01-24T19:04:12.552217460Z Caused by: client rate limiter Wait returned an error: context canceled 2024-01-24T19:04:12.552261513Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func3] ACTION REQUIRED: You'll need to manually remove cluster role with name 'kurtosis-api-a1367260310749f9a9406218a09fd9ae'!!!!!!! 2024-01-24T19:04:12.552359049Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func2] Creating the API container didn't complete successfully, so we tried to delete service account 'kurtosis-api' in namespace 'kt-rusty-forest' that we created but an error was thrown: 2024-01-24T19:04:12.552364299Z Failed to delete service account with name 'kurtosis-api' in namespace 'kt-rusty-forest' 2024-01-24T19:04:12.552367154Z --- at /home/circleci/project/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go:679 (KubernetesManager.RemoveServiceAccount) --- 2024-01-24T19:04:12.552369870Z Caused by: client rate limiter Wait returned an error: context canceled 2024-01-24T19:04:12.552413432Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func2] ACTION REQUIRED: You'll need to manually remove service account with name 'kurtosis-api'!!!!!!! 2024-01-24T19:04:12.552500759Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func1] Creating the api container didn't complete successfully, so we tried to delete Kubernetes service 'kurtosis-api' that we created but an error was thrown: 2024-01-24T19:04:12.552507441Z Failed to delete service 'kurtosis-api' with delete options '{TypeMeta:{Kind: APIVersion:} GracePeriodSeconds: Preconditions:nil OrphanDependents: PropagationPolicy:0x31a42d0 DryRun:[]}' in namespace 'kt-rusty-forest' 2024-01-24T19:04:12.552510908Z --- at /home/circleci/project/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go:234 (KubernetesManager.RemoveService) --- 2024-01-24T19:04:12.552514114Z Caused by: client rate limiter Wait returned an error: context canceled 2024-01-24T19:04:12.552562716Z ERRO[2024-01-24T19:04:12Z][kubernetes_kurtosis_backend_api_container_functions.go:func1] ACTION REQUIRED: You'll need to manually remove Kubernetes service with name 'kurtosis-api'!!!!!!! ``` --- ...urtosis_backend_api_container_functions.go | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go index 880a97352e..cabd190b13 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go @@ -2,6 +2,7 @@ package kubernetes_kurtosis_backend import ( "context" + "fmt" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/consts" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/shared_helpers" kubernetes_manager_consts "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/consts" @@ -162,7 +163,9 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( servicePorts, ) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred while creating the service with name '%s' in namespace '%s' with ports '%v'", apiContainerServiceName, enclaveNamespaceName, grpcPortInt32) + errMsg := fmt.Sprintf("An error occurred while creating the service with name '%s' in namespace '%s' with ports '%v'", apiContainerServiceName, enclaveNamespaceName, grpcPortInt32) + logrus.Errorf("%s. Error was:\n%s", errMsg, err) + return nil, stacktrace.Propagate(err, errMsg) } var shouldRemoveService = true defer func() { @@ -198,7 +201,9 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( serviceAccountLabels := shared_helpers.GetStringMapFromLabelMap(serviceAccountAttributes.GetLabels()) apiContainerServiceAccount, err := backend.kubernetesManager.CreateServiceAccount(ctx, serviceAccountName, enclaveNamespaceName, serviceAccountLabels) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred creating service account '%v' with labels '%+v' in namespace '%v'", serviceAccountName, serviceAccountLabels, enclaveNamespaceName) + errMsg := fmt.Sprintf("An error occurred creating service account '%v' with labels '%+v' in namespace '%v'", serviceAccountName, serviceAccountLabels, enclaveNamespaceName) + logrus.Errorf("%s. Error was:\n%s", errMsg, err) + return nil, stacktrace.Propagate(err, errMsg) } apiContainerServiceAccountName := apiContainerServiceAccount.GetName() shouldRemoveServiceAccount := true @@ -255,8 +260,10 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( apiContainerClusterRole, err := backend.kubernetesManager.CreateClusterRoles(ctx, clusterRoleName, clusterRolePolicyRules, clusterRoleLabels) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred creating cluster role '%v' with policy rules '%+v' "+ + errMsg := fmt.Sprintf("An error occurred creating cluster role '%v' with policy rules '%+v' "+ "and labels '%+v' in namespace '%v'", clusterRoleName, clusterRolePolicyRules, clusterRoleLabels, enclaveNamespaceName) + logrus.Errorf("%s. Error was:\n%s", errMsg, err) + return nil, stacktrace.Propagate(err, errMsg) } shouldRemoveClusterRole := true defer func() { @@ -296,8 +303,10 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( apiContainerClusterRoleBinding, err := backend.kubernetesManager.CreateClusterRoleBindings(ctx, clusterRoleBindingName, clusterRoleBindingsSubjects, clusterRoleBindingsRoleRef, clusterRoleBindingsLabels) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred creating cluster role bindings '%v' with subjects "+ + errMsg := fmt.Sprintf("An error occurred creating cluster role bindings '%v' with subjects "+ "'%+v' and role ref '%+v' in namespace '%v'", clusterRoleBindingName, clusterRoleBindingsSubjects, clusterRoleBindingsRoleRef, enclaveNamespaceName) + logrus.Errorf("%s. Error was:\n%s", errMsg, err) + return nil, stacktrace.Propagate(err, errMsg) } shouldRemoveClusterRoleBinding := true defer func() { @@ -353,8 +362,10 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( apiContainerRole, err := backend.kubernetesManager.CreateRole(ctx, roleName, enclaveNamespaceName, rolePolicyRules, roleLabels) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred creating role '%v' with policy rules '%+v' "+ + errMsg := fmt.Sprintf("An error occurred creating role '%v' with policy rules '%+v' "+ "and labels '%+v' in namespace '%v'", roleName, rolePolicyRules, roleLabels, enclaveNamespaceName) + logrus.Errorf("%s. Error was:\n%s", errMsg, err) + return nil, stacktrace.Propagate(err, errMsg) } shouldRemoveRole := true defer func() { @@ -394,8 +405,10 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( apiContainerRoleBinding, err := backend.kubernetesManager.CreateRoleBindings(ctx, roleBindingName, enclaveNamespaceName, roleBindingsSubjects, roleBindingsRoleRef, roleBindingsLabels) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred creating role bindings '%v' with subjects "+ + errMsg := fmt.Sprintf("An error occurred creating role bindings '%v' with subjects "+ "'%+v' and role ref '%+v' in namespace '%v'", roleBindingName, roleBindingsSubjects, roleBindingsRoleRef, enclaveNamespaceName) + logrus.Errorf("%s. Error was:\n%s", errMsg, err) + return nil, stacktrace.Propagate(err, errMsg) } shouldRemoveRoleBinding := true defer func() { @@ -422,7 +435,9 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( volumeLabelsStrs[key.GetString()] = value.GetString() } if _, err = backend.kubernetesManager.CreatePersistentVolumeClaim(ctx, enclaveNamespaceName, enclaveDataDirVolumeName, volumeLabelsStrs, enclaveDataDirVolumeSize); err != nil { - return nil, stacktrace.Propagate(err, "An error occurred creating the persistent volume claim for enclave data dir volume for enclave '%s'", enclaveDataDirVolumeName) + errMsg := fmt.Sprintf("An error occurred creating the persistent volume claim for enclave data dir volume for enclave '%s'", enclaveDataDirVolumeName) + logrus.Errorf("%s. Error was:\n%s", errMsg, err) + return nil, stacktrace.Propagate(err, errMsg) } shouldDeleteVolumeClaim := true @@ -464,7 +479,9 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( apiContainerRestartPolicy, ) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred while creating the pod with name '%s' in namespace '%s' with image '%s'", apiContainerPodName, enclaveNamespaceName, image) + errMsg := fmt.Sprintf("An error occurred while creating the pod with name '%s' in namespace '%s' with image '%s'", apiContainerPodName, enclaveNamespaceName, image) + logrus.Errorf("%s. Error was:\n%s", errMsg, err) + return nil, stacktrace.Propagate(err, errMsg) } var shouldRemovePod = true defer func() { From cc4458981da45dd51d7d2f8f497e2e3ac24f4313 Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:52:46 -0300 Subject: [PATCH 009/102] chore(main): release 0.86.11 (#2076) :robot: I have created a release *beep* *boop* --- ## [0.86.11](https://github.com/kurtosis-tech/kurtosis/compare/0.86.10...0.86.11) (2024-01-24) ### Features * adding error logs when K8s resource creation fails in the create enclave workflow ([#2074](https://github.com/kurtosis-tech/kurtosis/issues/2074)) ([a35e0a2](https://github.com/kurtosis-tech/kurtosis/commit/a35e0a2cee4bb771ccf095271d4b8bb08cc0bcf4)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 7 +++++++ LICENSE.md | 2 +- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- api/typescript/src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4df0e30a2b..9fde1fa38e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.86.11](https://github.com/kurtosis-tech/kurtosis/compare/0.86.10...0.86.11) (2024-01-24) + + +### Features + +* adding error logs when K8s resource creation fails in the create enclave workflow ([#2074](https://github.com/kurtosis-tech/kurtosis/issues/2074)) ([a35e0a2](https://github.com/kurtosis-tech/kurtosis/commit/a35e0a2cee4bb771ccf095271d4b8bb08cc0bcf4)) + ## [0.86.10](https://github.com/kurtosis-tech/kurtosis/compare/0.86.9...0.86.10) (2024-01-24) diff --git a/LICENSE.md b/LICENSE.md index a891d65686..47ac6257e7 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.10 +Licensed Work: Kurtosis 0.86.11 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 256810054b..fd2e433d2c 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.10" + KurtosisVersion = "0.86.11" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index ef8e4375fe..5c97daa2ee 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.10" +version = "0.86.11" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index a174485949..a15c238273 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.10", + "version": "0.86.11", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index eb1aebc96e..e45fa7b37a 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.10" +export const KURTOSIS_VERSION: string = "0.86.11" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 547554ca88..b795b6c4f2 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.10", + "version": "0.86.11", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index 6969b3d830..dbe6cd77f4 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.10", + "version": "0.86.11", "private": true, "homepage": ".", "dependencies": { @@ -9,7 +9,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.10", + "kurtosis-ui-components": "0.86.11", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "yaml": "^2.3.4" diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index 14dcd5d42e..abffe1ab9e 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.10", + "version": "0.86.11", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 9c2586e677..8ab5d9f703 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.10 +0.86.11 From 7045ca5d0ed89b8d56c04ce26f9ef01007eb1c25 Mon Sep 17 00:00:00 2001 From: Ben Gazzard Date: Thu, 25 Jan 2024 16:26:08 +0000 Subject: [PATCH 010/102] feat: emui add report a bug and info dialog to nav (#2080) ## Description: This PR fixes #2078 and fixes #2077 by adding the requested buttons to the navigation bar. ### Demo [Screencast from 25-01-24 16:05:48.webm](https://github.com/kurtosis-tech/kurtosis/assets/4419574/1d11a961-eece-4ada-9f2d-bfe998c68391) ## Is this change user facing? YES ## References (if applicable): * #2078 * #2077 --- .../web/packages/app/src/emui/Navbar.tsx | 62 ++++++++++++++++++- .../packages/components/src/Navigation.tsx | 8 ++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/enclave-manager/web/packages/app/src/emui/Navbar.tsx b/enclave-manager/web/packages/app/src/emui/Navbar.tsx index 409801a081..3176b6c93f 100644 --- a/enclave-manager/web/packages/app/src/emui/Navbar.tsx +++ b/enclave-manager/web/packages/app/src/emui/Navbar.tsx @@ -1,5 +1,22 @@ -import { NavButton, Navigation } from "kurtosis-ui-components"; +import { + Button, + Input, + InputGroup, + InputRightElement, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Text, +} from "@chakra-ui/react"; +import { CopyButton, NavButton, Navigation, NavigationDivider } from "kurtosis-ui-components"; +import { useState } from "react"; import { FiHome, FiPackage } from "react-icons/fi"; +import { GoBug } from "react-icons/go"; +import { MdInfoOutline } from "react-icons/md"; import { PiLinkSimpleBold } from "react-icons/pi"; import { Link, useLocation } from "react-router-dom"; import { KURTOSIS_CLOUD_CONNECT_URL } from "../client/constants"; @@ -8,6 +25,8 @@ import { useKurtosisClient } from "../client/enclaveManager/KurtosisClientContex export const Navbar = () => { const location = useLocation(); const kurtosisClient = useKurtosisClient(); + const [showAboutDialog, setShowAboutDialog] = useState(false); + const kurtosisVersion = process.env.REACT_APP_VERSION || "Unknown"; return ( @@ -26,6 +45,47 @@ export const Navbar = () => { } /> )} + + + } /> + + } onClick={() => setShowAboutDialog(true)} /> + + setShowAboutDialog(false)}> + + + About your Kurtosis Engine + + + Your Kurtosis engine version is: + + + + + + + + + + + + + + ); }; diff --git a/enclave-manager/web/packages/components/src/Navigation.tsx b/enclave-manager/web/packages/components/src/Navigation.tsx index 1e6a738abc..b7163f40c3 100644 --- a/enclave-manager/web/packages/components/src/Navigation.tsx +++ b/enclave-manager/web/packages/components/src/Navigation.tsx @@ -1,4 +1,4 @@ -import { Flex, IconButton, IconButtonProps, Image, Tooltip } from "@chakra-ui/react"; +import { Box, Flex, IconButton, IconButtonProps, Image, Tooltip } from "@chakra-ui/react"; import { PropsWithChildren } from "react"; import { useHref } from "react-router-dom"; @@ -22,13 +22,17 @@ export const Navigation = ({ children }: PropsWithChildren & NavigationProps) => - + {children} ); }; +export const NavigationDivider = () => { + return ; +}; + type NavButtonProps = Omit & { label: string; Icon: React.ReactElement; From 7a36ea35145bdf3f41a7b5c8e357ffd9a4cdfbc6 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Thu, 25 Jan 2024 16:27:20 +0000 Subject: [PATCH 011/102] feat: support passing toleration to kubernetes (#2071) We can now pass taints and tolerations to Kubernetes Closes #2048 --- .circleci/config.yml | 10 + .../engine_functions/create_engine.go | 4 + ...urtosis_backend_api_container_functions.go | 4 + .../start_user_services.go | 2 + .../kubernetes_manager/kubernetes_manager.go | 3 +- .../objects/service/service_config.go | 10 + .../objects/service/service_config_test.go | 13 ++ .../service_registration/repository_test.go | 1 + .../default_service_network_test.go | 1 + .../startosis_engine/kurtosis_builtins.go | 1 + .../add_service/add_service_shared.go | 1 + .../add_service/add_service_shared_test.go | 4 + .../tasks/tasks_shared.go | 1 + .../test_engine/add_service_framework_test.go | 1 + .../add_services_framework_test.go | 2 + .../service_config_image_build_spec_test.go | 1 + ...service_config_image_registry_spec_test.go | 1 + .../service_config_minimal_framework_test.go | 1 + .../service_config_toleration_test.go | 85 +++++++ .../test_engine/static_constants.go | 4 + .../service_config/service_config.go | 47 ++++ .../service_config/toleration.go | 218 ++++++++++++++++++ .../starlark-reference/service-config.md | 18 ++ .../starlark-reference/toleration.md | 24 ++ .../taints_and_toleration_test.star | 21 ++ 25 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_toleration_test.go create mode 100644 core/server/api_container/server/startosis_engine/kurtosis_types/service_config/toleration.go create mode 100644 docs/docs/api-reference/starlark-reference/toleration.md create mode 100644 internal_testsuites/starlark/taints_and_tolerations/taints_and_toleration_test.star diff --git a/.circleci/config.yml b/.circleci/config.yml index 9baca754e8..b9d61787a8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -223,6 +223,9 @@ parameters: starlark-test-args-file-test-main-star-relative-path: type: string default: "internal_testsuites/starlark/ci_tests/simple_arg_test/main.star" + starlark-test-taints-and-tolerations: + type: string + default: "internal_testsuites/starlark/taints_and_tolerations/taints_and_toleration_test.star" starlark-test-args-file-test-args-json-relative-path: type: string default: "internal_testsuites/starlark/ci_tests/simple_arg_test/args.json" @@ -562,6 +565,7 @@ jobs: paths: - "./<< pipeline.parameters.cli-dist-home-relative-dirpath >>" - "./<< pipeline.parameters.startosis-test-script-file-relative-path >>" + - "./<< pipeline.parameters.starlark-test-taints-and-tolerations >>" build_golang_testsuite: executor: ubuntu_vm @@ -909,6 +913,12 @@ jobs: # Execute docker compose import - run: "${KURTOSIS_BINPATH} import --enclave test-import --env << pipeline.parameters.dockerimport-cli-dotenv-relative-path >> << pipeline.parameters.dockerimport-cli-dockerfile-relative-path >>" + # This just tests that tolerations don't mess up Docker + # TODO add proper tests for Docker and Kubernetes that test + # Adding Toleration to a tainted cluster makes things work + # Not having toleration fails + - run: "${KURTOSIS_BINPATH} run --enclave with-tolerations << pipeline.parameters.workspace-with-cli-binary-and-images-mountpoint >>/<< pipeline.parameters.starlark-test-taints-and-tolerations >>" + # Execute engine functions - run: "${KURTOSIS_BINPATH} engine stop" - run: "${KURTOSIS_BINPATH} engine stop" diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go index ebafaf1d87..67bccf3a47 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go @@ -32,6 +32,9 @@ const ( var noWait *port_spec.Wait = nil +// TODO add support for passing toleration to Engine +var noToleration []apiv1.Toleration = nil + func CreateEngine( ctx context.Context, imageOrgAndRepo string, @@ -470,6 +473,7 @@ func createEnginePod( serviceAccountName, // Engine doesn't auto restart apiv1.RestartPolicyNever, + noToleration, ) if err != nil { return nil, nil, stacktrace.Propagate(err, "An error occurred while creating the pod with name '%s' in namespace '%s' with image '%s'", enginePodName, namespace, containerImageAndTag) diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go index cabd190b13..814e50ebf8 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go @@ -43,6 +43,9 @@ const ( var noWait *port_spec.Wait = nil +// TODO add support for passing toleration to APIC +var noTolerations []apiv1.Toleration = nil + // TODO: MIGRATE THIS FOLDER TO USE STRUCTURE OF USER_SERVICE_FUNCTIONS MODULE // Any of these values being nil indicates that the resource doesn't exist @@ -477,6 +480,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( apiContainerVolumes, apiContainerServiceAccountName, apiContainerRestartPolicy, + noTolerations, ) if err != nil { errMsg := fmt.Sprintf("An error occurred while creating the pod with name '%s' in namespace '%s' with image '%s'", apiContainerPodName, enclaveNamespaceName, image) diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go index 8d85648b92..577bd3f8c2 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go @@ -308,6 +308,7 @@ func createStartServiceOperation( minCpuAllocationMilliCpus := serviceConfig.GetMinCPUAllocationMillicpus() minMemoryAllocationMegabytes := serviceConfig.GetMinMemoryAllocationMegabytes() user := serviceConfig.GetUser() + tolerations := serviceConfig.GetTolerations() matchingObjectAndResources, found := servicesObjectsAndResources[serviceUuid] if !found { @@ -416,6 +417,7 @@ func createStartServiceOperation( podVolumes, userServiceServiceAccountName, restartPolicy, + tolerations, ) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred creating pod '%v' using image '%v'", podName, containerImageName) diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go index a5f56e6a88..2b37cd6f68 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/kubernetes_manager.go @@ -1048,6 +1048,7 @@ func (manager *KubernetesManager) CreatePod( podVolumes []apiv1.Volume, podServiceAccountName string, restartPolicy apiv1.RestartPolicy, + tolerations []apiv1.Toleration, ) (*apiv1.Pod, error) { podClient := manager.kubernetesClientSet.CoreV1().Pods(namespaceName) @@ -1096,7 +1097,7 @@ func (manager *KubernetesManager) CreatePod( Subdomain: "", Affinity: nil, SchedulerName: "", - Tolerations: nil, + Tolerations: tolerations, HostAliases: nil, PriorityClassName: "", Priority: nil, diff --git a/container-engine-lib/lib/backend_interface/objects/service/service_config.go b/container-engine-lib/lib/backend_interface/objects/service/service_config.go index ff9f4d8bb4..0b7b56b805 100644 --- a/container-engine-lib/lib/backend_interface/objects/service/service_config.go +++ b/container-engine-lib/lib/backend_interface/objects/service/service_config.go @@ -8,6 +8,7 @@ import ( "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user" "github.com/kurtosis-tech/stacktrace" + v1 "k8s.io/api/core/v1" ) // Config options for the underlying container of a service @@ -57,6 +58,9 @@ type privateServiceConfig struct { Labels map[string]string User *service_user.ServiceUser + + // TODO replace this with an abstraction that we own + Tolerations []v1.Toleration } func CreateServiceConfig( @@ -77,6 +81,7 @@ func CreateServiceConfig( minMemoryMegaBytes uint64, labels map[string]string, user *service_user.ServiceUser, + tolerations []v1.Toleration, ) (*ServiceConfig, error) { if err := ValidateServiceConfigLabels(labels); err != nil { @@ -102,6 +107,7 @@ func CreateServiceConfig( MinMemoryAllocationMegabytes: minMemoryMegaBytes, Labels: labels, User: user, + Tolerations: tolerations, } return &ServiceConfig{internalServiceConfig}, nil } @@ -176,6 +182,10 @@ func (serviceConfig *ServiceConfig) GetLabels() map[string]string { return serviceConfig.privateServiceConfig.Labels } +func (serviceConfig *ServiceConfig) GetTolerations() []v1.Toleration { + return serviceConfig.privateServiceConfig.Tolerations +} + func (serviceConfig *ServiceConfig) MarshalJSON() ([]byte, error) { return json.Marshal(serviceConfig.privateServiceConfig) } diff --git a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go index ad6087b4c9..5a2acb97de 100644 --- a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go +++ b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go @@ -8,6 +8,7 @@ import ( "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user" "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" "testing" "time" ) @@ -80,6 +81,7 @@ func getServiceConfigForTest(t *testing.T, imageName string) *ServiceConfig { "test-second-label-key": "test-second-label-value", }, testServiceUser(), + testToleration(), ) require.NoError(t, err) return serviceConfig @@ -187,3 +189,14 @@ func testServiceUser() *service_user.ServiceUser { su.SetGID(100) return su } + +func testToleration() []v1.Toleration { + tolerationSeconds := int64(6) + return []v1.Toleration{{ + Key: "testKey", + Operator: v1.TolerationOpEqual, + Value: "testValue", + Effect: v1.TaintEffectNoExecute, + TolerationSeconds: &tolerationSeconds, + }} +} diff --git a/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go b/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go index 6ba864fc65..85d497dff7 100644 --- a/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go +++ b/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go @@ -323,6 +323,7 @@ func getServiceConfigForTest(t *testing.T, imageName string) *service.ServiceCon "test-second-label-key": "test-second-label-value", }, nil, + nil, ) require.NoError(t, err) return serviceConfig diff --git a/core/server/api_container/server/service_network/default_service_network_test.go b/core/server/api_container/server/service_network/default_service_network_test.go index 72311f04ba..f97a53bfed 100644 --- a/core/server/api_container/server/service_network/default_service_network_test.go +++ b/core/server/api_container/server/service_network/default_service_network_test.go @@ -1222,6 +1222,7 @@ func testServiceConfig(t *testing.T, imageName string) *service.ServiceConfig { 0, map[string]string{}, nil, + nil, ) require.NoError(t, err) return serviceConfig diff --git a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go index 70eb4fb7e0..e8a4f6a8ac 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go @@ -114,5 +114,6 @@ func KurtosisTypeConstructors() []*starlark.Builtin { starlark.NewBuiltin(service_config.ImageBuildSpecTypeName, service_config.NewImageBuildSpecType().CreateBuiltin()), starlark.NewBuiltin(service_config.ImageRegistrySpecTypeName, service_config.NewImageRegistrySpec().CreateBuiltin()), starlark.NewBuiltin(service_config.UserTypeName, service_config.NewUserType().CreateBuiltin()), + starlark.NewBuiltin(service_config.TolerationTypeName, service_config.NewTolerationType().CreateBuiltin()), } } diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go index 93bea3a648..da32109423 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go @@ -216,6 +216,7 @@ func replaceMagicStrings( serviceConfig.GetMinMemoryAllocationMegabytes(), serviceConfig.GetLabels(), serviceConfig.GetUser(), + serviceConfig.GetTolerations(), ) if err != nil { return "", nil, stacktrace.Propagate(err, "An error occurred creating a service config") diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go index c63af9e09d..430f9701e8 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go @@ -54,6 +54,7 @@ func TestAddServiceShared_EntryPointArgsRuntimeValueAreReplaced(t *testing.T) { 0, map[string]string{}, nil, + nil, ) require.NoError(t, err) @@ -98,6 +99,7 @@ func TestAddServiceShared_CmdArgsRuntimeValueAreReplaced(t *testing.T) { 0, map[string]string{}, nil, + nil, ) require.NoError(t, err) @@ -144,6 +146,7 @@ func TestAddServiceShared_EnvVarsWithRuntimeValueAreReplaced(t *testing.T) { 0, map[string]string{}, nil, + nil, ) require.NoError(t, err) @@ -191,6 +194,7 @@ func TestAddServiceShared_ServiceNameWithRuntimeValuesAreReplaced(t *testing.T) 0, map[string]string{}, nil, + nil, ) require.NoError(t, err) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go index e8e245cd2d..e8addad386 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go @@ -272,6 +272,7 @@ func getServiceConfig(image string, filesArtifactExpansion *service_directory.Fi 0, map[string]string{}, nil, + nil, ) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred creating service config") diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go index 437ced821b..615790499f 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go @@ -50,6 +50,7 @@ func (suite *KurtosisPlanInstructionTestSuite) TestAddService() { 0, map[string]string{}, nil, + nil, ) require.NoError(suite.T(), err) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go index 7d8c097d7b..87a2247e3b 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go @@ -66,6 +66,7 @@ func (suite *KurtosisPlanInstructionTestSuite) TestAddServices() { 0, map[string]string{}, nil, + nil, ) require.NoError(suite.T(), err) @@ -90,6 +91,7 @@ func (suite *KurtosisPlanInstructionTestSuite) TestAddServices() { 0, map[string]string{}, nil, + nil, ) require.NoError(suite.T(), err) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go index 8d34280a93..037e9e441a 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go @@ -85,6 +85,7 @@ func (t *serviceConfigImageBuildSpecTestCase) Assert(typeValue builtin_argument. 0, map[string]string{}, nil, + nil, ) require.NoError(t, err) require.Equal(t, expectedServiceConfig, serviceConfig) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go index e881817fad..25d05586e9 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go @@ -75,6 +75,7 @@ func (t *serviceConfigImageRegistrySpecTest) Assert(typeValue builtin_argument.K 0, map[string]string{}, nil, + nil, ) require.NoError(t, err) require.Equal(t, expectedServiceConfig, serviceConfig) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go index cedd4374dc..a3b955d12a 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go @@ -62,6 +62,7 @@ func (t *serviceConfigMinimalTestCase) Assert(typeValue builtin_argument.Kurtosi 0, map[string]string{}, nil, + nil, ) require.NoError(t, err) require.Equal(t, expectedServiceConfig, serviceConfig) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_toleration_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_toleration_test.go new file mode 100644 index 0000000000..6780b2c474 --- /dev/null +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_toleration_test.go @@ -0,0 +1,85 @@ +package test_engine + +import ( + "fmt" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/service_config" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + "testing" +) + +type serviceConfigTolerationTest struct { + *testing.T + serviceNetwork *service_network.MockServiceNetwork + packageContentProvider *startosis_packages.MockPackageContentProvider +} + +func (suite *KurtosisTypeConstructorTestSuite) TestServiceConfigWithTolerationTest() { + suite.run(&serviceConfigTolerationTest{ + T: suite.T(), + serviceNetwork: suite.serviceNetwork, + packageContentProvider: suite.packageContentProvider, + }) +} + +func (t *serviceConfigTolerationTest) GetStarlarkCode() string { + toleration := fmt.Sprintf("%s(%s=%q, %s=%q, %s=%q, %s=%q, %s=%v)", + service_config.TolerationTypeName, + service_config.KeyAttr, + testTolerationKey, + service_config.OperatorAttr, + v1.TolerationOpEqual, + service_config.ValueAttr, + testTolerationValue, + service_config.EffectAttr, + v1.TaintEffectNoSchedule, + service_config.TolerationSecondsAttr, + testTolerationSeconds, + ) + return fmt.Sprintf("%s(%s=%q, %s=[%s])", + service_config.ServiceConfigTypeName, + service_config.ImageAttr, testContainerImageName, + service_config.TolerationsAttr, toleration) +} + +func (t *serviceConfigTolerationTest) Assert(typeValue builtin_argument.KurtosisValueType) { + serviceConfigStarlark, ok := typeValue.(*service_config.ServiceConfig) + require.True(t, ok) + + serviceConfig, interpretationErr := serviceConfigStarlark.ToKurtosisType( + t.serviceNetwork, + testModuleMainFileLocator, + testModulePackageId, + t.packageContentProvider, + testNoPackageReplaceOptions) + require.Nil(t, interpretationErr) + expectedTolerations := []v1.Toleration{{Key: testTolerationKey, Operator: v1.TolerationOpEqual, Value: testTolerationValue, Effect: v1.TaintEffectNoSchedule, TolerationSeconds: &testTolerationSeconds}} + expectedServiceConfig, err := service.CreateServiceConfig( + testContainerImageName, + nil, + nil, + map[string]*port_spec.PortSpec{}, + map[string]*port_spec.PortSpec{}, + nil, + nil, + map[string]string{}, + nil, + nil, + 0, + 0, + service_config.DefaultPrivateIPAddrPlaceholder, + 0, + 0, + map[string]string{}, + nil, + expectedTolerations, + ) + require.NoError(t, err) + require.Equal(t, expectedServiceConfig, serviceConfig) + require.Equal(t, expectedTolerations, serviceConfig.GetTolerations()) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go index c32daa7cba..9c3138eea9 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go @@ -117,4 +117,8 @@ var ( testServiceConfigLabelsKey1: testServiceConfigLabelsValue1, testServiceConfigLabelsKey2: testServiceConfigLabelsValue2, } + + testTolerationKey = "test-key" + testTolerationValue = "test-value" + testTolerationSeconds = int64(64) ) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go index 1b12a06547..e043956d98 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go @@ -21,6 +21,7 @@ import ( "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" "go.starlark.net/starlark" + v1 "k8s.io/api/core/v1" "math" "path" ) @@ -45,6 +46,7 @@ const ( MaxMemoryMegaBytesAttr = "max_memory" LabelsAttr = "labels" UserAttr = "user" + TolerationsAttr = "tolerations" DefaultPrivateIPAddrPlaceholder = "KURTOSIS_IP_ADDR_PLACEHOLDER" @@ -189,6 +191,14 @@ func NewServiceConfigType() *kurtosis_type_constructor.KurtosisTypeConstructor { ZeroValueProvider: builtin_argument.ZeroValueProvider[*User], Validator: nil, }, + { + Name: TolerationsAttr, + IsOptional: true, + ZeroValueProvider: builtin_argument.ZeroValueProvider[*starlark.List], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return nil + }, + }, }, }, @@ -454,6 +464,18 @@ func (config *ServiceConfig) ToKurtosisType( } } + var tolerations []v1.Toleration + tolerationsStarlarkList, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[*starlark.List](config.KurtosisValueTypeDefault, TolerationsAttr) + if interpretationErr != nil { + return nil, interpretationErr + } + if found { + tolerations, interpretationErr = convertTolerations(tolerationsStarlarkList) + if interpretationErr != nil { + return nil, interpretationErr + } + } + serviceConfig, err := service.CreateServiceConfig( imageName, maybeImageBuildSpec, @@ -472,6 +494,7 @@ func (config *ServiceConfig) ToKurtosisType( minMemory, labels, serviceUser, + tolerations, ) if err != nil { return nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred creating a service config") @@ -654,3 +677,27 @@ func convertImage( return imageName, nil, nil, nil } } + +func convertTolerations(tolerationsList *starlark.List) ([]v1.Toleration, *startosis_errors.InterpretationError) { + var outputValue []v1.Toleration + iterator := tolerationsList.Iterate() + defer iterator.Done() + var item starlark.Value + + var index = 0 + for iterator.Next(&item) { + toleration, ok := item.(*Toleration) + if !ok { + return nil, startosis_errors.NewInterpretationError("Expected item at index '%v' of the tolerations list passed via '%v' attr to be a '%v' but it wasn't", index, TolerationsAttr, TolerationTypeName) + } + + tolerationKubeType, err := toleration.ToKubeType() + if err != nil { + return nil, startosis_errors.WrapWithInterpretationError(err, "Error occurred while converting object at '%v' of '%v' list to internal type", index, TolerationsAttr) + } + outputValue = append(outputValue, *tolerationKubeType) + index += 1 + } + + return outputValue, nil +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/toleration.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/toleration.go new file mode 100644 index 0000000000..559b242a60 --- /dev/null +++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/toleration.go @@ -0,0 +1,218 @@ +package service_config + +import ( + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_type_constructor" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors" + "go.starlark.net/starlark" + v1 "k8s.io/api/core/v1" +) + +const ( + TolerationTypeName = "Toleration" + + KeyAttr = "key" + OperatorAttr = "operator" + ValueAttr = "value" + EffectAttr = "effect" + TolerationSecondsAttr = "toleration_seconds" +) + +var allowedOperatorValues = []string{string(v1.TolerationOpExists), string(v1.TolerationOpEqual)} +var allowedEffectValues = []string{string(v1.TaintEffectNoSchedule), string(v1.TaintEffectNoExecute), string(v1.TaintEffectPreferNoSchedule)} + +func NewTolerationType() *kurtosis_type_constructor.KurtosisTypeConstructor { + return &kurtosis_type_constructor.KurtosisTypeConstructor{ + KurtosisBaseBuiltin: &kurtosis_starlark_framework.KurtosisBaseBuiltin{ + Name: TolerationTypeName, + Arguments: []*builtin_argument.BuiltinArgument{ + { + Name: KeyAttr, + IsOptional: true, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.NonEmptyString(value, KeyAttr) + }, + }, + { + Name: OperatorAttr, + IsOptional: true, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.StringValues(value, OperatorAttr, allowedOperatorValues) + }, + }, + { + Name: ValueAttr, + IsOptional: true, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + }, + { + Name: EffectAttr, + IsOptional: true, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.StringValues(value, EffectAttr, allowedEffectValues) + }, + }, + { + Name: TolerationSecondsAttr, + IsOptional: true, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.Int], + Validator: nil, + }, + }, + Deprecation: nil, + }, + Instantiate: instantiateToleration, + } +} + +func instantiateToleration(arguments *builtin_argument.ArgumentValuesSet) (builtin_argument.KurtosisValueType, *startosis_errors.InterpretationError) { + kurtosisValueType, interpretationErr := kurtosis_type_constructor.CreateKurtosisStarlarkTypeDefault(TolerationTypeName, arguments) + if interpretationErr != nil { + return nil, interpretationErr + } + return &Toleration{ + kurtosisValueType, + }, nil +} + +type Toleration struct { + *kurtosis_type_constructor.KurtosisValueTypeDefault +} + +func (toleration *Toleration) Copy() (builtin_argument.KurtosisValueType, error) { + copiedValueType, err := toleration.KurtosisValueTypeDefault.Copy() + if err != nil { + return nil, err + } + return &Toleration{ + KurtosisValueTypeDefault: copiedValueType, + }, nil +} + +func (toleration *Toleration) GetKeyIfSet() (string, bool, *startosis_errors.InterpretationError) { + keyValue, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String]( + toleration.KurtosisValueTypeDefault, KeyAttr) + if interpretationErr != nil { + return "", false, interpretationErr + } + if !found { + return "", false, nil + } + return keyValue.GoString(), true, nil +} + +func (toleration *Toleration) GetOperatorIfSet() (string, bool, *startosis_errors.InterpretationError) { + attrValue, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String]( + toleration.KurtosisValueTypeDefault, OperatorAttr) + if interpretationErr != nil { + return "", false, interpretationErr + } + if !found { + return "", false, nil + } + return attrValue.GoString(), true, nil +} + +func (toleration *Toleration) GetValueIfExists() (string, bool, *startosis_errors.InterpretationError) { + attrValue, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String]( + toleration.KurtosisValueTypeDefault, ValueAttr) + if interpretationErr != nil { + return "", false, interpretationErr + } + if !found { + return "", false, nil + } + return attrValue.GoString(), true, nil +} + +func (toleration *Toleration) GetEffectIfExist() (string, bool, *startosis_errors.InterpretationError) { + attrValue, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String]( + toleration.KurtosisValueTypeDefault, EffectAttr) + if interpretationErr != nil { + return "", false, interpretationErr + } + if !found { + return "", false, nil + } + return attrValue.GoString(), true, nil +} + +func (toleration *Toleration) GetTolerationSecondsIfExists() (int64, bool, *startosis_errors.InterpretationError) { + attrValue, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.Int]( + toleration.KurtosisValueTypeDefault, TolerationSecondsAttr) + if interpretationErr != nil { + return 0, false, interpretationErr + } + if !found { + return 0, false, nil + } + attr, ok := attrValue.Int64() + if !ok { + return 0, false, startosis_errors.NewInterpretationError("Couldn't convert '%v' '%v' to int64", TolerationSecondsAttr, attr) + } + return attr, true, nil +} + +func (toleration *Toleration) ToKubeType() (*v1.Toleration, *startosis_errors.InterpretationError) { + //nolint :exhaustruct + returnValue := &v1.Toleration{} + + key, keyFound, err := toleration.GetKeyIfSet() + if err != nil { + return nil, err + } + + if keyFound { + returnValue.Key = key + } + + operator, operatorFound, err := toleration.GetOperatorIfSet() + if err != nil { + return nil, err + } + + if !keyFound && (!operatorFound || operator != string(v1.TolerationOpExists)) { + return nil, startosis_errors.NewInterpretationError("'%v' expects either '%v' to be set or for '%v' to be '%v'", TolerationTypeName, KeyAttr, OperatorAttr, v1.TolerationOpExists) + } + + if operatorFound { + returnValue.Operator = v1.TolerationOperator(operator) + } + + value, valueFound, err := toleration.GetValueIfExists() + if err != nil { + return nil, err + } + + if operatorFound && operator == string(v1.TolerationOpExists) && valueFound && value != "" { + return nil, startosis_errors.NewInterpretationError("'%v' cannot have non empty value '%v' for '%v' if '%v' is '%v'", TolerationTypeName, value, ValueAttr, OperatorAttr, operator) + } + + if valueFound { + returnValue.Value = value + } + + effect, effectFound, err := toleration.GetEffectIfExist() + if err != nil { + return nil, err + } + + if effectFound { + returnValue.Effect = v1.TaintEffect(effect) + } + + tolerationSeconds, tolerationSecondsFound, err := toleration.GetTolerationSecondsIfExists() + if err != nil { + return nil, err + } + + if tolerationSecondsFound { + returnValue.TolerationSeconds = &tolerationSeconds + } + + return returnValue, nil +} diff --git a/docs/docs/api-reference/starlark-reference/service-config.md b/docs/docs/api-reference/starlark-reference/service-config.md index 5d4a5534e5..b11a2edff9 100644 --- a/docs/docs/api-reference/starlark-reference/service-config.md +++ b/docs/docs/api-reference/starlark-reference/service-config.md @@ -159,6 +159,21 @@ config = ServiceConfig( # Note that the `gid` field is optional # OPTIONAL user = User(uid=0, gid=0), + + # An array of Toleration + # This refers to Kubernetes Tolerations https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + # This has no effect on Docker + # As of 2024-01-24 Taints and Tolerations to work with Kubernetes you need at least one untainted node + # OPTIONAL + tolerations = [ + Toleration( + key = "test-key", + value = "test-value", + operator = "Equal", + effect = "NoSchedule", + toleration_seconds = 64, + ) + ] ) ``` Note that `ImageBuildSpec` can only be used in packages and not standalone scripts as it relies on build context in package. @@ -209,6 +224,8 @@ labels: The `user` field expects a `User`[user] object being passed. +The `tolerations` field expects a list of `toleration[toleration]` object being passed. + [add-service-reference]: ./plan.md#add_service [directory]: ./directory.md @@ -217,3 +234,4 @@ The `user` field expects a `User`[user] object being passed. [locators]: ../../advanced-concepts/locators.md [package]: ../../advanced-concepts/packages.md [user]: ./user.md +[toleration]: ./toleration.md diff --git a/docs/docs/api-reference/starlark-reference/toleration.md b/docs/docs/api-reference/starlark-reference/toleration.md new file mode 100644 index 0000000000..2c50ba83a4 --- /dev/null +++ b/docs/docs/api-reference/starlark-reference/toleration.md @@ -0,0 +1,24 @@ +--- +title: Toleration +sidebar_label: Toleration +--- + +The `Toleration` constructor creates a `Toleration` object that represents a Kubernetes [Toleration](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) that +can be used with a [ServiceConfig][service-config] object. + +```python +toleration = Toleration( + key = "key", + operator = "Equal", + value = "value", + effect = "NoSchedule", + toleration_seconds = 64, +) +``` + +Note all fields are completely optional and follow the rules as laid out in the Kubernetes doc linked above. + +Note you need at least one untainted node to use Kurtosis with Kubernetes. + + +[service-config]: ./service-config.md \ No newline at end of file diff --git a/internal_testsuites/starlark/taints_and_tolerations/taints_and_toleration_test.star b/internal_testsuites/starlark/taints_and_tolerations/taints_and_toleration_test.star new file mode 100644 index 0000000000..4530a4f709 --- /dev/null +++ b/internal_testsuites/starlark/taints_and_tolerations/taints_and_toleration_test.star @@ -0,0 +1,21 @@ +def run(plan, with_tolerations=True): + if with_tolerations: + config = ServiceConfig( + image="kurtosistech/example-datastore-server", + tolerations=[ + Toleration( + key="foo", value="bar", operator="Equal", effect="NoSchedule" + ) + ], + ports={"grpc": PortSpec(number=1323, transport_protocol="TCP")}, + ) + else: + config = ServiceConfig( + image="kurtosistech/example-datastore-server", + ports={"grpc": PortSpec(number=1323, transport_protocol="TCP")}, + ) + + plan.add_service( + name="test-data-store", + config=config, + ) From 1fabfc06097ea37ee32777e4e2e83dd0d0ad7b24 Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Thu, 25 Jan 2024 14:57:11 -0300 Subject: [PATCH 012/102] chore(main): release 0.86.12 (#2084) :robot: I have created a release *beep* *boop* --- ## [0.86.12](https://github.com/kurtosis-tech/kurtosis/compare/0.86.11...0.86.12) (2024-01-25) ### Features * emui add report a bug and info dialog to nav ([#2080](https://github.com/kurtosis-tech/kurtosis/issues/2080)) ([7045ca5](https://github.com/kurtosis-tech/kurtosis/commit/7045ca5d0ed89b8d56c04ce26f9ef01007eb1c25)), closes [#2078](https://github.com/kurtosis-tech/kurtosis/issues/2078) [#2077](https://github.com/kurtosis-tech/kurtosis/issues/2077) * support passing toleration to kubernetes ([#2071](https://github.com/kurtosis-tech/kurtosis/issues/2071)) ([7a36ea3](https://github.com/kurtosis-tech/kurtosis/commit/7a36ea35145bdf3f41a7b5c8e357ffd9a4cdfbc6)), closes [#2048](https://github.com/kurtosis-tech/kurtosis/issues/2048) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 8 ++++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- api/typescript/src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 19 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fde1fa38e..5226b20ed4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.86.12](https://github.com/kurtosis-tech/kurtosis/compare/0.86.11...0.86.12) (2024-01-25) + + +### Features + +* emui add report a bug and info dialog to nav ([#2080](https://github.com/kurtosis-tech/kurtosis/issues/2080)) ([7045ca5](https://github.com/kurtosis-tech/kurtosis/commit/7045ca5d0ed89b8d56c04ce26f9ef01007eb1c25)), closes [#2078](https://github.com/kurtosis-tech/kurtosis/issues/2078) [#2077](https://github.com/kurtosis-tech/kurtosis/issues/2077) +* support passing toleration to kubernetes ([#2071](https://github.com/kurtosis-tech/kurtosis/issues/2071)) ([7a36ea3](https://github.com/kurtosis-tech/kurtosis/commit/7a36ea35145bdf3f41a7b5c8e357ffd9a4cdfbc6)), closes [#2048](https://github.com/kurtosis-tech/kurtosis/issues/2048) + ## [0.86.11](https://github.com/kurtosis-tech/kurtosis/compare/0.86.10...0.86.11) (2024-01-24) diff --git a/LICENSE.md b/LICENSE.md index 47ac6257e7..c12661f723 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.11 +Licensed Work: Kurtosis 0.86.12 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-01-24 +Change Date: 2028-01-25 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index fd2e433d2c..5a7448da50 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.11" + KurtosisVersion = "0.86.12" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 5c97daa2ee..d54b2dafdf 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.11" +version = "0.86.12" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index a15c238273..bd02128164 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.11", + "version": "0.86.12", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index e45fa7b37a..e581e78d94 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.11" +export const KURTOSIS_VERSION: string = "0.86.12" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index b795b6c4f2..14f40dc221 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.11", + "version": "0.86.12", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index dbe6cd77f4..3768c18526 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.11", + "version": "0.86.12", "private": true, "homepage": ".", "dependencies": { @@ -9,7 +9,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.11", + "kurtosis-ui-components": "0.86.12", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "yaml": "^2.3.4" diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index abffe1ab9e..de3188fc42 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.11", + "version": "0.86.12", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 8ab5d9f703..923806c10c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.11 +0.86.12 From dcf61bdde2af7872f568169b9cf57a1fb63bb4ad Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Fri, 26 Jan 2024 12:44:05 +0000 Subject: [PATCH 013/102] docs: added more detailed docs for toleration (#2091) Docs weren't up to standards! Improved it --- .../starlark-reference/service-config.md | 5 +-- .../starlark-reference/toleration.md | 32 ++++++++++++++++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/docs/docs/api-reference/starlark-reference/service-config.md b/docs/docs/api-reference/starlark-reference/service-config.md index b11a2edff9..d94fca7794 100644 --- a/docs/docs/api-reference/starlark-reference/service-config.md +++ b/docs/docs/api-reference/starlark-reference/service-config.md @@ -164,6 +164,7 @@ config = ServiceConfig( # This refers to Kubernetes Tolerations https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ # This has no effect on Docker # As of 2024-01-24 Taints and Tolerations to work with Kubernetes you need at least one untainted node + # Refer to the Toleration docs linked near the end of the page to learn more # OPTIONAL tolerations = [ Toleration( @@ -222,9 +223,9 @@ labels: ``` ::: -The `user` field expects a `User`[user] object being passed. +The `user` field expects a [`User`][user] object being passed. -The `tolerations` field expects a list of `toleration[toleration]` object being passed. +The `tolerations` field expects a list of [`Toleration`][toleration] objects being passed. [add-service-reference]: ./plan.md#add_service diff --git a/docs/docs/api-reference/starlark-reference/toleration.md b/docs/docs/api-reference/starlark-reference/toleration.md index 2c50ba83a4..fa4eb9688a 100644 --- a/docs/docs/api-reference/starlark-reference/toleration.md +++ b/docs/docs/api-reference/starlark-reference/toleration.md @@ -8,11 +8,33 @@ can be used with a [ServiceConfig][service-config] object. ```python toleration = Toleration( - key = "key", - operator = "Equal", - value = "value", - effect = "NoSchedule", - toleration_seconds = 64, + # key is the taint key that the toleration applies to. Empty means match all taint keys. + # If the key is empty, operator must be Exists; this combination means to match all values and all keys. + # OPTIONAL + key = "key", + + # operator represents a key's relationship to the value. + # Valid operators are Exists and Equal. Defaults to Equal. + # Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + # OPTIONAL + operator = "Equal", + + # value is the taint value the toleration matches to. + # If the operator is Exists, the value should be empty, otherwise just a regular string. + # OPTIONAL + value = "value", + + # effect indicates the taint effect to match. Empty means match all taint effects. + # When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + # OPTIONAL + effect = "NoSchedule", + + # toleration_seconds represents the period of time the toleration (which must be + # of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + # it is not set, which means tolerate the taint forever (do not evict). Zero and + # negative values will be treated as 0 (evict immediately) by the system. + # OPTIONAL + toleration_seconds = 64, ) ``` From 7bbe4796a5b1e952fb85d7cf89136b34ad35f70a Mon Sep 17 00:00:00 2001 From: Tedi Mitiku Date: Fri, 26 Jan 2024 10:22:46 -0500 Subject: [PATCH 014/102] feat: add run docker compose with kurtosis guide (#2085) ## Description: Guide for running docker compose with Kurtosis. ## Is this change user facing? YES ## References: https://github.com/kurtosis-tech/kurtosis/pull/2043 https://github.com/kurtosis-tech/kurtosis/pull/2001 --- docs/docs/get-started/quickstart.md | 5 + docs/docs/guides/running-docker-compose.md | 134 +++++++++++++++++++++ docs/static/img/guides/compose-env.jpg | Bin 0 -> 269270 bytes 3 files changed, 139 insertions(+) create mode 100644 docs/docs/guides/running-docker-compose.md create mode 100644 docs/static/img/guides/compose-env.jpg diff --git a/docs/docs/get-started/quickstart.md b/docs/docs/get-started/quickstart.md index f112b6b3e8..3b0c84ebe4 100644 --- a/docs/docs/get-started/quickstart.md +++ b/docs/docs/get-started/quickstart.md @@ -19,6 +19,10 @@ Before you get started, make sure you have: This guide will have you writing Kurtosis Starlark. You can optionally install [the VSCode plugin](https://marketplace.visualstudio.com/items?itemName=Kurtosis.kurtosis-extension) to get syntax highlighting, autocomplete, and documentation. ::: +:::tip Have a Docker Compose setup? +Check out this [guide][running-docker-compose] to run your Docker Compose setup with Kurtosis in one line! +::: + Run a basic package from Github --------------------------------------- @@ -201,6 +205,7 @@ Now that you've use the Kurtosis CLI to run a package, inspect the resulting env [basic-files-artifact]: ../get-started/basic-concepts.md#files-artifact [write-your-first-package]: ../get-started/write-your-first-package.md [running-in-k8s]: ../guides/running-in-k8s.md +[running-docker-compose]: ../guides/running-docker-compose.md [architecture-explanation]: ../advanced-concepts/architecture.md diff --git a/docs/docs/guides/running-docker-compose.md b/docs/docs/guides/running-docker-compose.md new file mode 100644 index 0000000000..0561d99d96 --- /dev/null +++ b/docs/docs/guides/running-docker-compose.md @@ -0,0 +1,134 @@ +--- +title: Running Docker Compose setup with Kurtosis +sidebar_label: Running Docker Compose +slug: /docker-compose +sidebar_position: 13 +--- + +This guide assumes that you have [Kurtosis installed](../get-started/installing-the-cli.md) and a project with a `docker-compose.yml`, `compose.yml`, `docker_compose.yml` or `.yaml` equivalents. + +:::info Experimental Feature +This is an experimental feature still under development and some Docker Compose setups are not yet supported. +See below for list of features not yet supported in Kurtosis. If you'd like support for your Docker Compose setup, let us know on [Github](https://github.com/kurtosis-tech/kurtosis/issues)! +::: + +### 1. Setup Docker Compose project + +Navigate to the root of your project with a `docker-compose.yml` or if the project is hosted on Github, grab the Github link where the Docker Compose exists. For this guide, we'll go off of the [`nextcloud-redis-mariadb`](https://github.com/docker/awesome-compose/blob/master/nextcloud-redis-mariadb/compose.yaml) compose in the [`awesome-compose`](https://github.com/docker/awesome-compose/tree/master) repo. + +``` +services: + nc: + image: nextcloud:apache + restart: always + ports: + - 80:80 + volumes: + - nc_data:/var/www/html + networks: + - redisnet + - dbnet + environment: + - REDIS_HOST=redis + - MYSQL_HOST=db + - MYSQL_DATABASE=nextcloud + - MYSQL_USER=nextcloud + - MYSQL_PASSWORD=nextcloud + redis: + image: redis:alpine + restart: always + networks: + - redisnet + expose: + - 6379 + db: + image: mariadb:10.5 + command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW + restart: always + volumes: + - db_data:/var/lib/mysql + networks: + - dbnet + environment: + - MYSQL_DATABASE=nextcloud + - MYSQL_USER=nextcloud + - MYSQL_ROOT_PASSWORD=nextcloud + - MYSQL_PASSWORD=nextcloud + expose: + - 3306 +volumes: + db_data: + nc_data: +networks: + dbnet: + redisnet: +``` + +### 2. Run! + +In the root of your project, run the following command: + +``` +kurtosis run . +``` +OR using github link: +``` +kurtosis run github.com/awesome-compose/nextcloud-redis-mariadb +``` + +Behind the scenes, Kurtosis will interpret your Docker Compose setup as a Kurtosis [package](../get-started/basic-concepts.md#package) and convert it into [starlark](../advanced-concepts/starlark.md) that is executed on an [enclave](../get-started/basic-concepts.md#enclave). The output will look like this: + +```bash +INFO[2024-01-25T13:56:29-05:00] Creating a new enclave for Starlark to run inside... +INFO[2024-01-25T13:56:33-05:00] Enclave 'blue-ravine' created successfully + +Container images used in this run: +> nextcloud:apache - remotely downloaded +> mariadb:10.5 - locally built +> redis:alpine - locally cached + +> add_service name="db" config=ServiceConfig(image="mariadb:10.5", files={"/var/lib/mysql": Directory(persistent_key="db--volume0")}, cmd=["--transaction-isolation=READ-COMMITTED", "--binlog-format=ROW"], env_vars={"MYSQL_DATABASE": "nextcloud", "MYSQL_PASSWORD": "nextcloud", "MYSQL_ROOT_PASSWORD": "nextcloud", "MYSQL_USER": "nextcloud"}) +Service 'db' added with service UUID '7010d01344e34a7c9f061d4fa43e5e0d' + +> add_service name="nc" config=ServiceConfig(image="nextcloud:apache", ports={"port0": PortSpec(number=80, transport_protocol="TCP")}, files={"/var/www/html": Directory(persistent_key="nc--volume0")}, env_vars={"MYSQL_DATABASE": "nextcloud", "MYSQL_HOST": "db", "MYSQL_PASSWORD": "nextcloud", "MYSQL_USER": "nextcloud", "REDIS_HOST": "redis"}) +Service 'nc' added with service UUID 'c30843ea60b8459c8841565a11be5dde' + +> add_service name="redis" config=ServiceConfig(image="redis:alpine", env_vars={}) +Service 'redis' added with service UUID '26dceba158004fdcb8d5dba035a6c4dd' + +Starlark code successfully run. No output was returned. + +Made with Kurtosis - https://kurtosis.com +INFO[2024-01-25T13:56:46-05:00] ==================================================== +INFO[2024-01-25T13:56:46-05:00] || Created enclave: blue-ravine || +INFO[2024-01-25T13:56:46-05:00] ==================================================== +Name: blue-ravine +UUID: 04fa7472e566 +Status: RUNNING +Creation Time: Thu, 25 Jan 2024 13:56:29 EST +Flags: + +========================================= Files Artifacts ========================================= +UUID Name + +========================================== User Services ========================================== +UUID Name Ports Status +7010d01344e3 db RUNNING +c30843ea60b8 nc port0: 80/tcp -> 127.0.0.1:62938 RUNNING +26dceba15800 redis RUNNING +``` + +Congrats! You now have your Docker Compose setup running in Kurtosis. Run `kurtosis web` to view your environment in Kurtosis' GUI. Additionally, you can configure the Kurtosis engine to run over Kubernetes and run the same command to get your Docker Compose setup running in a K8s cluster. Check out this [guide](./running-in-k8s.md) on how to se tup Kurtosis over K8s! + +![compose env](../../static/img/guides/compose-env.jpg) + +### Notes on Docker Compose to Kurtosis conversion + +- Named volumes are converted to a [Persistent Directory](../api-reference/starlark-reference/directory.md) in Kurtosis - a Kurtosis managed directory that persists on services through multiple runs +- Kurtosis handles creating an isolated network inside an enclave. If the `network` key specifies custom networks, the config is ignored, potentially altering network behavior in the environment +- Services names with a `_` must be renamed as Kurtosis naming follows [RFC-1035](../best-practices.md) standard +- Service level [`env_file`](https://docs.docker.com/compose/compose-file/05-services/#env_file) key is not yet supported ([`environment`](https://docs.docker.com/compose/compose-file/compose-file-v3/#environment) is supported) +- [`secret`](https://docs.docker.com/compose/compose-file/05-services/#secrets) key is not yet supported +- For`volumes`, absolute path mappings (e.g. `/opt/data:/var/lib/mysql`) are not supported as Kurtosis packages cannot reference files outside a Kurtosis package. You can move the contents inside the package and convert to a relative path so Kurtosis can access the files +- Referencing a service's name in a hostname elsewhere in the Docker Compose (e.g. `postgres://db:5431`) is not supported +- `reservations` for `cpus` and `memory` are supported, but other container level config such as `restart`, `limits`, etc. are not yet supported \ No newline at end of file diff --git a/docs/static/img/guides/compose-env.jpg b/docs/static/img/guides/compose-env.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3f17ce070a440bc3a05ca2cc7c1413b4bf68753f GIT binary patch literal 269270 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

<=$wJ9;xdsro#<;oA>P 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 Date: Tue, 30 Jan 2024 15:30:10 -0300 Subject: [PATCH 015/102] chore: debug CLI configuration and readme docs (#2086) ## Description: debug CLI configuration and readme docs ## Is this change user facing? NO ## References (if applicable): This is for Kurtosis devs --- .run/CLI-remote-debug.run.xml | 6 ++ README.md | 42 +++++++++++++- cli/cli/scripts/_constants.sh | 2 + cli/cli/scripts/build.sh | 6 +- cli/cli/scripts/debug-cli.sh | 59 ++++++++++++++++++++ flake.nix | 4 +- readme-static-files/dlv-terminal.png | Bin 0 -> 43475 bytes readme-static-files/goland-breakpoint.png | Bin 0 -> 27220 bytes readme-static-files/goland-debug-button.png | Bin 0 -> 2661 bytes readme-static-files/goland-debug-panel.png | Bin 0 -> 38905 bytes scripts/{set_ktdev.sh => set_kt_alias.sh} | 7 ++- 11 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 .run/CLI-remote-debug.run.xml create mode 100755 cli/cli/scripts/debug-cli.sh create mode 100644 readme-static-files/dlv-terminal.png create mode 100644 readme-static-files/goland-breakpoint.png create mode 100644 readme-static-files/goland-debug-button.png create mode 100644 readme-static-files/goland-debug-panel.png rename scripts/{set_ktdev.sh => set_kt_alias.sh} (74%) diff --git a/.run/CLI-remote-debug.run.xml b/.run/CLI-remote-debug.run.xml new file mode 100644 index 0000000000..0992c3f0a7 --- /dev/null +++ b/.run/CLI-remote-debug.run.xml @@ -0,0 +1,6 @@ + + + + diff --git a/README.md b/README.md index 05b2d7471b..2505b7af91 100644 --- a/README.md +++ b/README.md @@ -346,7 +346,7 @@ A Kurtosis engine is running with the following info: Version: 0.X.Y ``` -1. Run `test.sh` script +3. Run `test.sh` script ```console $ ./internal_testsuites/scripts/test.sh @@ -371,6 +371,45 @@ alias kurtosis="$(pwd)/cli/cli/scripts/launch-cli.sh" kurtosis enclave add ``` +Run Debug Instructions (for Golang code so far) +---------------------------------------------- + +For running CLI with Golang remote debug: + +1. Build the CLI dev binary and run the command you want to debug (kurtosis version in this example), this will start the debug server and will wait for a client connection +```bash +cli/cli/scripts/build.sh +source ./scripts/set_kt_alias.sh +ktdebug version +``` +2. Open the command's file you want to debug +3. Add the breakpoint in the line where you want to stop the cursor + +4. Then choose the "CLI-remote-debug" run configuration in the "run panel" +5. Press the "debug" button + +6. Use the debug panel to inspect the variables value and continue with the debug flow + + + +For running CLI with Delve debug client: +1. Build the CLI dev binary and run the command you want to debug (kurtosis version in this example), but first pass "dlv-terminal" as the first argument (this will start the Delve client in the terminal) +```bash +cli/cli/scripts/build.sh +source ./scripts/set_kt_alias.sh +ktdebug dlv-terminal version +``` +2. You can add a new breakpoint using the terminal client and the `break` command +```bash +(dlv) break version.run:1 +``` +3. You can move the cursor to the breakpoint created in the previous step with the `continue` command +```bash +(dlv) continue +``` + +4. You can see [more Delve commands here][delve-docs] + @@ -383,3 +422,4 @@ kurtosis enclave add [twitter]: https://twitter.com/KurtosisTech [starlark-explanation]: https://docs.kurtosis.com/explanations/starlark [stackoverflow-2022-developer-survey--other-tools]: https://survey.stackoverflow.co/2022/#most-popular-technologies-tools-tech-prof +[delve-docs]: https://github.com/go-delve/delve/blob/master/Documentation/cli/README.md \ No newline at end of file diff --git a/cli/cli/scripts/_constants.sh b/cli/cli/scripts/_constants.sh index afc69f6236..1138c88cd6 100644 --- a/cli/cli/scripts/_constants.sh +++ b/cli/cli/scripts/_constants.sh @@ -6,3 +6,5 @@ CLI_BINARY_FILENAME="kurtosis" # Name of the CLI binary GO_ARCH_ENV_AMD64_VALUE="amd64" GO_DEFAULT_AMD64_ENV="v1" + +CLI_DEBUG_SERVER_PORT=50101 # Makes sure it same port configured in .run/CLI-remote-debug.run.xml file diff --git a/cli/cli/scripts/build.sh b/cli/cli/scripts/build.sh index b6b4eaaa1c..adc8474bad 100755 --- a/cli/cli/scripts/build.sh +++ b/cli/cli/scripts/build.sh @@ -86,11 +86,11 @@ fi exit 1 fi if "${should_publish_arg}"; then - goreleaser_verb_and_flags="release --rm-dist" + goreleaser_verb_and_flags="release --clean" elif "${should_build_all_arg}" ; then - goreleaser_verb_and_flags="release --rm-dist --snapshot" + goreleaser_verb_and_flags="release --clean --snapshot" else - goreleaser_verb_and_flags="build --rm-dist --snapshot --single-target" + goreleaser_verb_and_flags="build --clean --snapshot --single-target" fi if ! GORELEASER_CURRENT_TAG=$(cat $root_dirpath/version.txt) goreleaser ${goreleaser_verb_and_flags}; then echo "Error: Couldn't build the CLI binary for the current OS/arch" >&2 diff --git a/cli/cli/scripts/debug-cli.sh b/cli/cli/scripts/debug-cli.sh new file mode 100755 index 0000000000..8a2984dea4 --- /dev/null +++ b/cli/cli/scripts/debug-cli.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# 2021-07-08 WATERMARK, DO NOT REMOVE - This script was generated from the Kurtosis Bash script template + +set -euo pipefail # Bash "strict mode" +script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cli_module_dirpath="$(dirname "${script_dirpath}")" + + +# ================================================================================================== +# Constants +# ================================================================================================== +source "${script_dirpath}/_constants.sh" + +# ================================================================================================== +# Main Logic +# ================================================================================================== + +# Check and install Go Delve +# it should be installed if you are using Nix, but it's better to check first because the script could be executed from outside the Nix scope +echo "Checking for Delve..." +if ! dlv version ; then + echo "Delve binary was no found, we recommend you whether to execute this script inside the Nix scope (if you are using Nix) or install the Delve binary with this cmd: 'go install github.com/go-delve/delve/cmd/dlv@latest'" + exit 1 +fi +echo "...it was found." + +goarch="$(go env GOARCH)" +goos="$(go env GOOS)" + +architecture_dirname="${GORELEASER_CLI_BUILD_ID}_${goos}_${goarch}" + +if [ "${goarch}" == "${GO_ARCH_ENV_AMD64_VALUE}" ]; then + goamd64="$(go env GOAMD64)" + if [ "${goamd64}" == "" ]; then + goamd64="${GO_DEFAULT_AMD64_ENV}" + fi + architecture_dirname="${architecture_dirname}_${goamd64}" +fi + +cli_binary_filepath="${cli_module_dirpath}/${GORELEASER_OUTPUT_DIRNAME}/${architecture_dirname}/${CLI_BINARY_FILENAME}" + +if ! [ -f "${cli_binary_filepath}" ]; then + echo "Error: Expected a CLI binary to have been built by Goreleaser at '${cli_binary_filepath}' but none exists" >&2 + exit 1 +fi + +# The funky ${1+"${@}"} incantation is how you feed arguments exactly as-is to a child script in Bash +# ${*} loses quoting and ${@} trips set -e if no arguments are passed, so this incantation says, "if and only if +# ${1} exists, evaluate ${@}" +cli_arguments=${1+"${@}"} +first_argument=$1 +headless_val="true" +if [ "${first_argument}" == "dlv-terminal" ]; then + headless_val="false" + # The CLI's arguments start from the second position + cli_arguments=${2+"${@:2}"} +fi + +dlv --listen="127.0.0.1:${CLI_DEBUG_SERVER_PORT}" --headless="${headless_val}" --api-version=2 --check-go-version=false --only-same-user=false exec "${cli_binary_filepath}" ${cli_arguments} diff --git a/flake.nix b/flake.nix index cc9aebb766..b35be15268 100644 --- a/flake.nix +++ b/flake.nix @@ -72,9 +72,9 @@ @@ @@@ @@@ @@ @@@ @@@@@@@@ \u001b[0m - Starting Kurtosis dev shell. Setup the alias to local compiled Kurtosis cli command "ktdev" by running: + Starting Kurtosis dev shell. Setup the alias to local compiled Kurtosis cli command "ktdev" and "ktdebug" by running: \e[32m - source ./scripts/set_ktdev.sh + source ./scripts/set_kt_alias.sh \e[0m ' ''; diff --git a/readme-static-files/dlv-terminal.png b/readme-static-files/dlv-terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..9b523bd72124583b7725ea8bb122a97b14494f5c GIT binary patch literal 43475 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ⅇb=K$Q+am26++U96P~kXm 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 literal 0 HcmV?d00001 diff --git a/readme-static-files/goland-debug-button.png b/readme-static-files/goland-debug-button.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae80d81d8156a8f57de049e7e043eeb6f787ff3 GIT binary patch literal 2661 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 Date: Wed, 31 Jan 2024 09:52:21 -0300 Subject: [PATCH 017/102] refactor: engine start and restart CLI commands refactor, using the Kurtosis cmd framework (#2096) ## Description: engine start and restart CLI commands refactor, using the Kurtosis cmd framework ## Is this change user facing? NO ## References (if applicable): Doing this refactor as a part of the "enabling debug server in engine's container" these changes will prepare the CLI to receive a new `debug_mode` persistent flag in these commands (will send a new PR for enabling this flag) --- .../lowlevel/flags/flag_type.go | 1 + .../lowlevel/flags/flag_type_processors.go | 29 +++++ .../lowlevel/flags/flagtype_enumer.go | 28 +++-- .../lowlevel/flags/parsed_flags.go | 13 +++ cli/cli/commands/engine/engine.go | 4 +- cli/cli/commands/engine/restart/restart.go | 109 +++++++++-------- cli/cli/commands/engine/start/start.go | 110 ++++++++++-------- 7 files changed, 188 insertions(+), 106 deletions(-) diff --git a/cli/cli/command_framework/lowlevel/flags/flag_type.go b/cli/cli/command_framework/lowlevel/flags/flag_type.go index 5c870124f7..363e480a34 100644 --- a/cli/cli/command_framework/lowlevel/flags/flag_type.go +++ b/cli/cli/command_framework/lowlevel/flags/flag_type.go @@ -5,6 +5,7 @@ type FlagType int const ( FlagType_String FlagType = iota // This is intentionally the first value, meaning it will be the emptyval/default + FlagType_Uint8 FlagType_Uint32 FlagType_Bool ) diff --git a/cli/cli/command_framework/lowlevel/flags/flag_type_processors.go b/cli/cli/command_framework/lowlevel/flags/flag_type_processors.go index b325f4fbcf..92082d9b4a 100644 --- a/cli/cli/command_framework/lowlevel/flags/flag_type_processors.go +++ b/cli/cli/command_framework/lowlevel/flags/flag_type_processors.go @@ -8,6 +8,7 @@ import ( const ( uintBase = 10 + uint8Bits = 8 uint32Bits = 32 ) @@ -24,6 +25,7 @@ type flagTypeProcessor func( // Completeness enforced via unit test var AllFlagTypeProcessors = map[FlagType]flagTypeProcessor{ FlagType_String: processStringFlag, + FlagType_Uint8: processUint8Flag, FlagType_Uint32: processUint32Flag, FlagType_Bool: processBoolFlag, } @@ -45,6 +47,33 @@ func processStringFlag( return nil } +func processUint8Flag( + flagKey string, + shorthand string, + defaultValueStr string, + usage string, + cobraFlagSet *flag.FlagSet, +) error { + defaultValueUint64, err := strconv.ParseUint(defaultValueStr, uintBase, uint8Bits) + if err != nil { + return stacktrace.Propagate( + err, + "Could not parse default value string '%v' of flag '%v' to a uint8 using base %v and bits %v", + defaultValueStr, + flagKey, + uintBase, + uint8Bits, + ) + } + cobraFlagSet.Uint8P( + flagKey, + shorthand, + uint8(defaultValueUint64), + usage, + ) + return nil +} + func processUint32Flag( flagKey string, shorthand string, diff --git a/cli/cli/command_framework/lowlevel/flags/flagtype_enumer.go b/cli/cli/command_framework/lowlevel/flags/flagtype_enumer.go index cdfc9caf54..29dc16438b 100644 --- a/cli/cli/command_framework/lowlevel/flags/flagtype_enumer.go +++ b/cli/cli/command_framework/lowlevel/flags/flagtype_enumer.go @@ -7,11 +7,11 @@ import ( "strings" ) -const _FlagTypeName = "stringuint32bool" +const _FlagTypeName = "stringuint8uint32bool" -var _FlagTypeIndex = [...]uint8{0, 6, 12, 16} +var _FlagTypeIndex = [...]uint8{0, 6, 11, 17, 21} -const _FlagTypeLowerName = "stringuint32bool" +const _FlagTypeLowerName = "stringuint8uint32bool" func (i FlagType) String() string { if i < 0 || i >= FlagType(len(_FlagTypeIndex)-1) { @@ -25,25 +25,29 @@ func (i FlagType) String() string { func _FlagTypeNoOp() { var x [1]struct{} _ = x[FlagType_String-(0)] - _ = x[FlagType_Uint32-(1)] - _ = x[FlagType_Bool-(2)] + _ = x[FlagType_Uint8-(1)] + _ = x[FlagType_Uint32-(2)] + _ = x[FlagType_Bool-(3)] } -var _FlagTypeValues = []FlagType{FlagType_String, FlagType_Uint32, FlagType_Bool} +var _FlagTypeValues = []FlagType{FlagType_String, FlagType_Uint8, FlagType_Uint32, FlagType_Bool} var _FlagTypeNameToValueMap = map[string]FlagType{ _FlagTypeName[0:6]: FlagType_String, _FlagTypeLowerName[0:6]: FlagType_String, - _FlagTypeName[6:12]: FlagType_Uint32, - _FlagTypeLowerName[6:12]: FlagType_Uint32, - _FlagTypeName[12:16]: FlagType_Bool, - _FlagTypeLowerName[12:16]: FlagType_Bool, + _FlagTypeName[6:11]: FlagType_Uint8, + _FlagTypeLowerName[6:11]: FlagType_Uint8, + _FlagTypeName[11:17]: FlagType_Uint32, + _FlagTypeLowerName[11:17]: FlagType_Uint32, + _FlagTypeName[17:21]: FlagType_Bool, + _FlagTypeLowerName[17:21]: FlagType_Bool, } var _FlagTypeNames = []string{ _FlagTypeName[0:6], - _FlagTypeName[6:12], - _FlagTypeName[12:16], + _FlagTypeName[6:11], + _FlagTypeName[11:17], + _FlagTypeName[17:21], } // FlagTypeString retrieves an enum value from the enum constants string name. diff --git a/cli/cli/command_framework/lowlevel/flags/parsed_flags.go b/cli/cli/command_framework/lowlevel/flags/parsed_flags.go index 8997940d35..3d9efb8bc8 100644 --- a/cli/cli/command_framework/lowlevel/flags/parsed_flags.go +++ b/cli/cli/command_framework/lowlevel/flags/parsed_flags.go @@ -24,6 +24,18 @@ func (flags *ParsedFlags) GetString(name string) (string, error) { } return value, nil } + +func (flags *ParsedFlags) GetUint8(name string) (uint8, error) { + value, err := flags.cmdFlagsSet.GetUint8(name) + if err != nil { + return 0, stacktrace.Propagate(err, + "An error occurred getting uint8 flag '%v'", + name, + ) + } + return value, nil +} + func (flags *ParsedFlags) GetUint32(name string) (uint32, error) { value, err := flags.cmdFlagsSet.GetUint32(name) if err != nil { @@ -34,6 +46,7 @@ func (flags *ParsedFlags) GetUint32(name string) (uint32, error) { } return value, nil } + func (flags *ParsedFlags) GetBool(name string) (bool, error) { value, err := flags.cmdFlagsSet.GetBool(name) if err != nil { diff --git a/cli/cli/commands/engine/engine.go b/cli/cli/commands/engine/engine.go index 31e2d0e6c4..41f10137be 100644 --- a/cli/cli/commands/engine/engine.go +++ b/cli/cli/commands/engine/engine.go @@ -19,9 +19,9 @@ var EngineCmd = &cobra.Command{ } func init() { - EngineCmd.AddCommand(start.StartCmd) + EngineCmd.AddCommand(start.StartCmd.MustGetCobraCommand()) EngineCmd.AddCommand(status.StatusCmd) EngineCmd.AddCommand(stop.StopCmd) - EngineCmd.AddCommand(restart.RestartCmd) + EngineCmd.AddCommand(restart.RestartCmd.MustGetCobraCommand()) EngineCmd.AddCommand(logs.EngineLogsCmd.MustGetCobraCommand()) } diff --git a/cli/cli/commands/engine/restart/restart.go b/cli/cli/commands/engine/restart/restart.go index cc3e73a59f..6038f9a756 100644 --- a/cli/cli/commands/engine/restart/restart.go +++ b/cli/cli/commands/engine/restart/restart.go @@ -3,6 +3,9 @@ package restart import ( "context" "fmt" + "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" "github.com/kurtosis-tech/kurtosis/cli/cli/commands/engine/common" "github.com/kurtosis-tech/kurtosis/cli/cli/defaults" @@ -10,71 +13,80 @@ import ( "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/logrus_log_levels" "github.com/kurtosis-tech/stacktrace" "github.com/sirupsen/logrus" - "github.com/spf13/cobra" + "strconv" "strings" ) const ( - engineVersionArg = "version" - logLevelArg = "log-level" - enclavePoolSizeFlag = "enclave-pool-size" + engineVersionFlagKey = "version" + logLevelFlagKey = "log-level" + enclavePoolSizeFlagKey = "enclave-pool-size" defaultEngineVersion = "" restartEngineOnSameVersionIfAnyRunning = false ) -var engineVersion string -var logLevelStr string -var enclavePoolSize uint8 - -// RestartCmd Suppressing exhaustruct requirement because this struct has ~40 properties -// nolint: exhaustruct -var RestartCmd = &cobra.Command{ - Use: command_str_consts.EngineRestartCmdStr, - Short: "Restart the Kurtosis engine", - Long: "Stops any existing Kurtosis engine, then starts a new one", - RunE: run, -} - -func init() { - RestartCmd.Flags().StringVar( - &engineVersion, - engineVersionArg, - defaultEngineVersion, - "The version (Docker tag) of the Kurtosis engine that should be started (blank will start the default version)", - ) - RestartCmd.Flags().StringVar( - &logLevelStr, - logLevelArg, - defaults.DefaultEngineLogLevel.String(), - fmt.Sprintf( - "The level that the started engine should log at (%v)", - strings.Join( - logrus_log_levels.GetAcceptableLogLevelStrs(), - "|", +var RestartCmd = &lowlevel.LowlevelKurtosisCommand{ + CommandStr: command_str_consts.EngineRestartCmdStr, + ShortDescription: "Restart the Kurtosis engine", + LongDescription: "Stops any existing Kurtosis engine, then starts a new one", + Args: nil, + Flags: []*flags.FlagConfig{ + { + Key: engineVersionFlagKey, + Usage: "The version (Docker tag) of the Kurtosis engine that should be started (blank will start the default version)", + Shorthand: "", + Type: flags.FlagType_String, + Default: defaultEngineVersion, + }, + { + Key: logLevelFlagKey, + Usage: fmt.Sprintf( + "The level that the started engine should log at (%v)", + strings.Join( + logrus_log_levels.GetAcceptableLogLevelStrs(), + "|", + ), + ), + Shorthand: "", + Type: flags.FlagType_String, + Default: defaults.DefaultEngineLogLevel.String(), + }, + { + Key: enclavePoolSizeFlagKey, + Usage: fmt.Sprintf( + "The enclave pool size, the default value is '%v' which means it will be disabled. CAUTION: This is only available for Kubernetes, and this command will fail if you want to use it for Docker.", + defaults.DefaultEngineEnclavePoolSize, ), - ), - ) - RestartCmd.Flags().Uint8Var( - &enclavePoolSize, - enclavePoolSizeFlag, - defaults.DefaultEngineEnclavePoolSize, - fmt.Sprintf( - "The enclave pool size, the default value is '%v' which means it will be disabled. CAUTION: This is only available for Kubernetes, and this command will fail if you want to use it for Docker.", - defaults.DefaultEngineEnclavePoolSize, - ), - ) + Shorthand: "", + Type: flags.FlagType_Uint8, + Default: strconv.Itoa(int(defaults.DefaultEngineEnclavePoolSize)), + }, + }, + PreValidationAndRunFunc: nil, + RunFunc: run, + PostValidationAndRunFunc: nil, } -func run(cmd *cobra.Command, args []string) error { +func run(_ context.Context, flags *flags.ParsedFlags, _ *args.ParsedArgs) error { ctx := context.Background() + enclavePoolSize, err := flags.GetUint8(enclavePoolSizeFlagKey) + if err != nil { + return stacktrace.Propagate(err, "Expected a integer flag with key '%v' but none was found; this is an error in Kurtosis!", enclavePoolSizeFlagKey) + } + if err := common.ValidateEnclavePoolSizeFlag(enclavePoolSize); err != nil { - return stacktrace.Propagate(err, "An error occurred validating the '%v' flag", enclavePoolSizeFlag) + return stacktrace.Propagate(err, "An error occurred validating the '%v' flag", enclavePoolSizeFlagKey) } logrus.Infof("Restarting Kurtosis engine...") + logLevelStr, err := flags.GetString(logLevelFlagKey) + if err != nil { + return stacktrace.Propagate(err, "An error occurred while getting the Kurtosis engine log level using flag with key '%v'; this is a bug in Kurtosis", logLevelFlagKey) + } + logLevel, err := logrus.ParseLevel(logLevelStr) if err != nil { return stacktrace.Propagate(err, "An error occurred parsing log level string '%v'", logLevelStr) @@ -85,6 +97,11 @@ func run(cmd *cobra.Command, args []string) error { return stacktrace.Propagate(err, "An error occurred creating an engine manager.") } + engineVersion, err := flags.GetString(engineVersionFlagKey) + if err != nil { + return stacktrace.Propagate(err, "An error occurred while getting the Kurtosis engine Container Version using flag with key '%v'; this is a bug in Kurtosis", engineVersionFlagKey) + } + var engineClientCloseFunc func() error var restartEngineErr error _, engineClientCloseFunc, restartEngineErr = engineManager.RestartEngineIdempotently(ctx, logLevel, engineVersion, restartEngineOnSameVersionIfAnyRunning, enclavePoolSize) diff --git a/cli/cli/commands/engine/start/start.go b/cli/cli/commands/engine/start/start.go index 5ed451d42f..585ab4edb4 100644 --- a/cli/cli/commands/engine/start/start.go +++ b/cli/cli/commands/engine/start/start.go @@ -3,6 +3,9 @@ package start import ( "context" "fmt" + "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" "github.com/kurtosis-tech/kurtosis/cli/cli/commands/engine/common" "github.com/kurtosis-tech/kurtosis/cli/cli/defaults" @@ -11,68 +14,77 @@ import ( "github.com/kurtosis-tech/kurtosis/kurtosis_version" "github.com/kurtosis-tech/stacktrace" "github.com/sirupsen/logrus" - "github.com/spf13/cobra" + "strconv" "strings" ) const ( - engineVersionArg = "version" - logLevelArg = "log-level" - enclavePoolSizeFlag = "enclave-pool-size" + engineVersionFlagKey = "version" + logLevelFlagKey = "log-level" + enclavePoolSizeFlagKey = "enclave-pool-size" defaultEngineVersion = "" kurtosisTechEngineImagePrefix = "kurtosistech/engine" imageVersionDelimiter = ":" ) -var engineVersion string -var logLevelStr string -var enclavePoolSize uint8 - -// StartCmd Suppressing exhaustruct requirement because this struct has ~40 properties -// nolint: exhaustruct -var StartCmd = &cobra.Command{ - Use: command_str_consts.EngineStartCmdStr, - Short: "Starts the Kurtosis engine", - Long: "Starts the Kurtosis engine, doing nothing if an engine is already running", - RunE: run, -} - -func init() { - StartCmd.Flags().StringVar( - &engineVersion, - engineVersionArg, - defaultEngineVersion, - "The version (Docker tag) of the Kurtosis engine that should be started (blank will start the default version)", - ) - StartCmd.Flags().StringVar( - &logLevelStr, - logLevelArg, - defaults.DefaultEngineLogLevel.String(), - fmt.Sprintf( - "The level that the started engine should log at (%v)", - strings.Join( - logrus_log_levels.GetAcceptableLogLevelStrs(), - "|", +var StartCmd = &lowlevel.LowlevelKurtosisCommand{ + CommandStr: command_str_consts.EngineStartCmdStr, + ShortDescription: "Starts the Kurtosis engine", + LongDescription: "Starts the Kurtosis engine, doing nothing if an engine is already running", + Args: nil, + Flags: []*flags.FlagConfig{ + { + Key: engineVersionFlagKey, + Usage: "The version (Docker tag) of the Kurtosis engine that should be started (blank will start the default version)", + Shorthand: "", + Type: flags.FlagType_String, + Default: defaultEngineVersion, + }, + { + Key: logLevelFlagKey, + Usage: fmt.Sprintf( + "The level that the started engine should log at (%v)", + strings.Join( + logrus_log_levels.GetAcceptableLogLevelStrs(), + "|", + ), + ), + Shorthand: "", + Type: flags.FlagType_String, + Default: defaults.DefaultEngineLogLevel.String(), + }, + { + Key: enclavePoolSizeFlagKey, + Usage: fmt.Sprintf( + "The enclave pool size, the default value is '%v' which means it will be disabled. CAUTION: This is only available for Kubernetes, and this command will fail if you want to use it for Docker.", + defaults.DefaultEngineEnclavePoolSize, ), - ), - ) - StartCmd.Flags().Uint8Var( - &enclavePoolSize, - enclavePoolSizeFlag, - defaults.DefaultEngineEnclavePoolSize, - fmt.Sprintf( - "The enclave pool size, the default value is '%v' which means it will be disabled. CAUTION: This is only available for Kubernetes, and this command will fail if you want to use it for Docker.", - defaults.DefaultEngineEnclavePoolSize, - ), - ) + Shorthand: "", + Type: flags.FlagType_Uint8, + Default: strconv.Itoa(int(defaults.DefaultEngineEnclavePoolSize)), + }, + }, + PreValidationAndRunFunc: nil, + RunFunc: run, + PostValidationAndRunFunc: nil, } -func run(cmd *cobra.Command, args []string) error { +func run(_ context.Context, flags *flags.ParsedFlags, _ *args.ParsedArgs) error { ctx := context.Background() + enclavePoolSize, err := flags.GetUint8(enclavePoolSizeFlagKey) + if err != nil { + return stacktrace.Propagate(err, "Expected a integer flag with key '%v' but none was found; this is an error in Kurtosis!", enclavePoolSizeFlagKey) + } + if err := common.ValidateEnclavePoolSizeFlag(enclavePoolSize); err != nil { - return stacktrace.Propagate(err, "An error occurred validating the '%v' flag", enclavePoolSizeFlag) + return stacktrace.Propagate(err, "An error occurred validating the '%v' flag", enclavePoolSizeFlagKey) + } + + logLevelStr, err := flags.GetString(logLevelFlagKey) + if err != nil { + return stacktrace.Propagate(err, "An error occurred while getting the Kurtosis engine log level using flag with key '%v'; this is a bug in Kurtosis", logLevelFlagKey) } logLevel, err := logrus.ParseLevel(logLevelStr) @@ -87,6 +99,12 @@ func run(cmd *cobra.Command, args []string) error { var engineClientCloseFunc func() error var startEngineErr error + + engineVersion, err := flags.GetString(engineVersionFlagKey) + if err != nil { + return stacktrace.Propagate(err, "An error occurred while getting the Kurtosis engine Container Version using flag with key '%v'; this is a bug in Kurtosis", engineVersionFlagKey) + } + 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) From 7bbb2bc7a5487b05f91089634beed5e83e3329de Mon Sep 17 00:00:00 2001 From: Edgar Gomes Date: Wed, 31 Jan 2024 13:46:10 -0300 Subject: [PATCH 018/102] feat: add nodejs devtools to Nix (#2099) ## Description: Manage nodeJS devtools with Nix ## Is this change user facing? NO --- .../api_container_service.pb.go | 4 +- .../api_container_service_grpc.pb.go | 2 +- .../engine_service.pb.go | 2 +- .../engine_service_grpc.pb.go | 2 +- api/golang/go.mod | 2 +- api/scripts/protobuf-bindings-generator.sh | 24 +- .../api_container_service_grpc_web_pb.d.ts | 4 +- .../api_container_service_grpc_web_pb.js | 2 +- .../api_container_service_pb.d.ts | 2 +- .../api_container_service_connect.d.ts | 2 +- .../connect/api_container_service_connect.js | 2 +- .../connect/api_container_service_pb.d.ts | 2 +- .../connect/api_container_service_pb.js | 2 +- .../connect/engine_service_connect.d.ts | 2 +- .../connect/engine_service_connect.js | 2 +- .../connect/engine_service_pb.d.ts | 2 +- .../connect/engine_service_pb.js | 2 +- .../engine_service_grpc_web_pb.d.ts | 4 +- .../engine_service_grpc_web_pb.js | 2 +- .../engine_service_pb.d.ts | 4 +- core/server/go.mod | 2 +- .../kurtosis_enclave_manager_api.pb.go | 2 +- .../kurtosis_enclave_manager_api_grpc.pb.go | 2 +- .../api/scripts/regenerate-proto-bindings.sh | 25 +- .../src/kurtosis_enclave_manager_api_pb.ts | 2 +- flake.lock | 20 +- flake.nix | 17 +- internal_testsuites/golang/go.mod | 2 +- nix-pkgs/node-tools/README.md | 16 + nix-pkgs/node-tools/default.nix | 15 + nix-pkgs/node-tools/node-env.nix | 684 ++++++++++++++++++ nix-pkgs/node-tools/node-packages.nix | 541 ++++++++++++++ .../package-lock.json | 212 ++++-- nix-pkgs/node-tools/package.json | 12 + nix-pkgs/openapi-ts/default.nix | 37 - nix-pkgs/openapi-ts/package.json | 8 - nix-pkgs/protoc-gen-ts.nix | 148 ---- 37 files changed, 1520 insertions(+), 295 deletions(-) create mode 100644 nix-pkgs/node-tools/README.md create mode 100644 nix-pkgs/node-tools/default.nix create mode 100644 nix-pkgs/node-tools/node-env.nix create mode 100644 nix-pkgs/node-tools/node-packages.nix rename nix-pkgs/{openapi-ts => node-tools}/package-lock.json (60%) create mode 100644 nix-pkgs/node-tools/package.json delete mode 100644 nix-pkgs/openapi-ts/default.nix delete mode 100644 nix-pkgs/openapi-ts/package.json delete mode 100644 nix-pkgs/protoc-gen-ts.nix diff --git a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go index dcc77bcf10..a8d4f05aea 100644 --- a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go +++ b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.3 +// protoc v4.24.4 // source: api_container_service.proto package kurtosis_core_rpc_api_bindings @@ -785,8 +785,10 @@ type RunStarlarkPackageArgs struct { MainFunctionName *string `protobuf:"bytes,10,opt,name=main_function_name,json=mainFunctionName,proto3,oneof" json:"main_function_name,omitempty"` ExperimentalFeatures []KurtosisFeatureFlag `protobuf:"varint,11,rep,packed,name=experimental_features,json=experimentalFeatures,proto3,enum=api_container_api.KurtosisFeatureFlag" json:"experimental_features,omitempty"` // Defaults to empty + // Deprecated: This value isn't used in the APIC anymore CloudInstanceId *string `protobuf:"bytes,12,opt,name=cloud_instance_id,json=cloudInstanceId,proto3,oneof" json:"cloud_instance_id,omitempty"` // Defaults to empty + // Deprecated: This value isn't used in the APIC anymore CloudUserId *string `protobuf:"bytes,13,opt,name=cloud_user_id,json=cloudUserId,proto3,oneof" json:"cloud_user_id,omitempty"` // Defaults to empty ImageDownloadMode *ImageDownloadMode `protobuf:"varint,14,opt,name=image_download_mode,json=imageDownloadMode,proto3,enum=api_container_api.ImageDownloadMode,oneof" json:"image_download_mode,omitempty"` diff --git a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc.pb.go b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc.pb.go index 6120e72c02..780145bdc4 100644 --- a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc.pb.go +++ b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.24.3 +// - protoc v4.24.4 // source: api_container_service.proto package kurtosis_core_rpc_api_bindings diff --git a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go index 7438fcfd5a..13d42f6d3b 100644 --- a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go +++ b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.3 +// protoc v4.24.4 // source: engine_service.proto package kurtosis_engine_rpc_api_bindings diff --git a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc.pb.go b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc.pb.go index d147ca1326..030fc811cf 100644 --- a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc.pb.go +++ b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.24.3 +// - protoc v4.24.4 // source: engine_service.proto package kurtosis_engine_rpc_api_bindings diff --git a/api/golang/go.mod b/api/golang/go.mod index f13ae39464..fd79c3c18b 100644 --- a/api/golang/go.mod +++ b/api/golang/go.mod @@ -17,7 +17,7 @@ require ( github.com/kurtosis-tech/kurtosis/cloud/api/golang v0.0.0-20230803130419-099ee7a4e3dc github.com/kurtosis-tech/kurtosis/contexts-config-store v0.0.0-20230818184218-f4e3e773463b github.com/kurtosis-tech/kurtosis/grpc-file-transfer/golang v0.0.0-20230803130419-099ee7a4e3dc - github.com/kurtosis-tech/kurtosis/utils v0.0.0-20240104153602-385833de9d76 // this version needs to be fixed for outside people to import api/golang + github.com/kurtosis-tech/kurtosis/utils v0.0.0-20240104153602-385833de9d76 // this version needs to be fixed for outside people to import api/golang github.com/kurtosis-tech/stacktrace v0.0.0-20211028211901-1c67a77b5409 github.com/labstack/echo/v4 v4.11.3 github.com/oapi-codegen/runtime v1.1.0 diff --git a/api/scripts/protobuf-bindings-generator.sh b/api/scripts/protobuf-bindings-generator.sh index 55f5e10858..464d094c29 100755 --- a/api/scripts/protobuf-bindings-generator.sh +++ b/api/scripts/protobuf-bindings-generator.sh @@ -41,6 +41,8 @@ GO_MOD_FILE_MODULE_KEYWORD="module" # Keyword in the go.mod file for specifying # ----------------------------------------- Typescript ----------------------------------------------- NODE_GRPC_TOOLS_PROTOC_BIN_FILENAME="grpc_tools_node_protoc" # For some reason, Node gRPC has its own 'protoc' binary NODE_GRPC_TOOLS_PROTOC_PLUGIN_BIN_FILENAME="grpc_tools_node_protoc_plugin" # The name of the plugin binary that will be used by 'protoc' +NODE_ES_TOOLS_PROTOC_BIN_FILENAME="protoc-gen-es" +NODE_CONNECT_ES_TOOLS_PROTOC_BIN_FILENAME="protoc-gen-connect-es" NODE_GRPC_TOOLS_INSTALL_COMMAND="npm install -g grpc-tools" WEB_GRPC_PROTOC_BIN_FILENAME="protoc-gen-grpc-web" WEB_GRPC_INSTALL_COMMAND="brew install protoc-gen-grpc-web" @@ -222,6 +224,24 @@ generate_typescript_bindings() { echo "Error: Got an empty filepath when looking for the Node gRPC tools protoc plugin binary '${NODE_GRPC_TOOLS_PROTOC_PLUGIN_BIN_FILENAME}'; have you installed the tools with '${NODE_GRPC_TOOLS_PROTOC_PLUGIN_BIN_FILENAME}'?" >&2 return 1 fi + + if ! node_es_protoc_bin_filepath="$(which "${NODE_ES_TOOLS_PROTOC_BIN_FILENAME}")"; then + echo "Error: Couldn't find Node gRPC tools protoc plugin binary '${NODE_ES_TOOLS_PROTOC_BIN_FILENAME}' on the PATH" >&2 + return 1 + fi + if [ -z "${node_es_protoc_bin_filepath}" ]; then + echo "Error: Got an empty filepath when looking for the Node gRPC tools protoc plugin binary '${NODE_ES_TOOLS_PROTOC_BIN_FILENAME}'" >&2 + return 1 + fi + + if ! node_connect_es_protoc_bin_filepath="$(which "${NODE_CONNECT_ES_TOOLS_PROTOC_BIN_FILENAME}")"; then + echo "Error: Couldn't find Node gRPC tools protoc plugin binary '${NODE_CONNECT_ES_TOOLS_PROTOC_BIN_FILENAME}' on the PATH" >&2 + return 1 + fi + if [ -z "${node_connect_es_protoc_bin_filepath}" ]; then + echo "Error: Got an empty filepath when looking for the Node gRPC tools protoc plugin binary '${NODE_CONNECT_ES_TOOLS_PROTOC_BIN_FILENAME}'" >&2 + return 1 + fi for input_filepath in $(find "${input_abs_dirpath}" -type f -name "*${PROTOBUF_FILE_EXT}"); do # NOTE: Generating Node bindings @@ -249,10 +269,10 @@ generate_typescript_bindings() { if ! "${node_protoc_bin_filepath}" \ -I="${input_abs_dirpath}" \ - "--plugin=protoc-gen-es=${api_dir_path}/typescript/node_modules/.bin/protoc-gen-es" \ + "--plugin=protoc-gen-es=${node_es_protoc_bin_filepath}" \ "--es_out=${output_abs_dirpath}/connect" \ "--es_opt=target=js+dts" \ - "--plugin=protoc-gen-connect-es=${api_dir_path}/typescript/node_modules/.bin/protoc-gen-connect-es" \ + "--plugin=protoc-gen-connect-es=${node_connect_es_protoc_bin_filepath}" \ "--connect-es_out=${output_abs_dirpath}/connect" \ "--connect-es_opt=target=js+dts" \ "${input_filepath}"; then diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.d.ts index f5505b0e0e..c7a6afdd19 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.d.ts @@ -1,7 +1,7 @@ import * as grpcWeb from 'grpc-web'; -import * as api_container_service_pb from './api_container_service_pb'; -import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; +import * as api_container_service_pb from './api_container_service_pb'; // proto import: "api_container_service.proto" +import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; // proto import: "google/protobuf/empty.proto" export class ApiContainerServiceClient { diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.js b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.js index 07732d2fbc..403dc2af4e 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.js +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.js @@ -6,7 +6,7 @@ // Code generated by protoc-gen-grpc-web. DO NOT EDIT. // versions: -// protoc-gen-grpc-web v1.4.2 +// protoc-gen-grpc-web v1.5.0 // protoc v3.19.1 // source: api_container_service.proto diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts index fad8e7d501..bff0c23421 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts @@ -1,6 +1,6 @@ import * as jspb from 'google-protobuf' -import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; +import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; // proto import: "google/protobuf/empty.proto" export class Port extends jspb.Message { diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.d.ts index 69d4435f66..6441630968 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.d.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-connect-es v0.12.0 with parameter "target=js+dts" +// @generated by protoc-gen-connect-es v0.13.2 with parameter "target=js+dts" // @generated from file api_container_service.proto (package api_container_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.js b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.js index 8b3148f5a6..fc66916490 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.js +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.js @@ -1,4 +1,4 @@ -// @generated by protoc-gen-connect-es v0.12.0 with parameter "target=js+dts" +// @generated by protoc-gen-connect-es v0.13.2 with parameter "target=js+dts" // @generated from file api_container_service.proto (package api_container_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts index fc8c1e404c..bbef713a48 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v1.3.0 with parameter "target=js+dts" +// @generated by protoc-gen-es v1.5.1 with parameter "target=js+dts" // @generated from file api_container_service.proto (package api_container_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js index 8201357329..eed1454734 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v1.3.0 with parameter "target=js+dts" +// @generated by protoc-gen-es v1.5.1 with parameter "target=js+dts" // @generated from file api_container_service.proto (package api_container_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.d.ts index 173b34286b..04235e5f69 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.d.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-connect-es v0.12.0 with parameter "target=js+dts" +// @generated by protoc-gen-connect-es v0.13.2 with parameter "target=js+dts" // @generated from file engine_service.proto (package engine_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.js b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.js index f441326504..7d45190ac5 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.js +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.js @@ -1,4 +1,4 @@ -// @generated by protoc-gen-connect-es v0.12.0 with parameter "target=js+dts" +// @generated by protoc-gen-connect-es v0.13.2 with parameter "target=js+dts" // @generated from file engine_service.proto (package engine_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts index 3471151cd1..f0ab878b01 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v1.3.0 with parameter "target=js+dts" +// @generated by protoc-gen-es v1.5.1 with parameter "target=js+dts" // @generated from file engine_service.proto (package engine_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js index 18661eb149..6a8a9cfd9c 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v1.3.0 with parameter "target=js+dts" +// @generated by protoc-gen-es v1.5.1 with parameter "target=js+dts" // @generated from file engine_service.proto (package engine_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.d.ts index 523a2a2d20..4a41f55da9 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.d.ts @@ -1,7 +1,7 @@ import * as grpcWeb from 'grpc-web'; -import * as engine_service_pb from './engine_service_pb'; -import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; +import * as engine_service_pb from './engine_service_pb'; // proto import: "engine_service.proto" +import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; // proto import: "google/protobuf/empty.proto" export class EngineServiceClient { diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.js b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.js index 6a18e31707..6a8a4bad27 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.js +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.js @@ -6,7 +6,7 @@ // Code generated by protoc-gen-grpc-web. DO NOT EDIT. // versions: -// protoc-gen-grpc-web v1.4.2 +// protoc-gen-grpc-web v1.5.0 // protoc v3.19.1 // source: engine_service.proto diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts index 43f5a23ceb..ee7c174bda 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts @@ -1,7 +1,7 @@ import * as jspb from 'google-protobuf' -import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; -import * as google_protobuf_timestamp_pb from 'google-protobuf/google/protobuf/timestamp_pb'; +import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; // proto import: "google/protobuf/empty.proto" +import * as google_protobuf_timestamp_pb from 'google-protobuf/google/protobuf/timestamp_pb'; // proto import: "google/protobuf/timestamp.proto" export class GetEngineInfoResponse extends jspb.Message { diff --git a/core/server/go.mod b/core/server/go.mod index e9cb828175..7bae767697 100644 --- a/core/server/go.mod +++ b/core/server/go.mod @@ -45,6 +45,7 @@ require ( go.starlark.net v0.0.0-20230224151120-c52844e64a10 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/sync v0.3.0 + k8s.io/api v0.27.2 ) require ( @@ -149,7 +150,6 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.27.2 // indirect k8s.io/apimachinery v0.27.2 // indirect k8s.io/client-go v0.27.2 // indirect k8s.io/klog/v2 v2.90.1 // indirect diff --git a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api.pb.go b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api.pb.go index 639bd5e9a0..99288ac7ee 100644 --- a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api.pb.go +++ b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.3 +// protoc v4.24.4 // source: kurtosis_enclave_manager_api.proto package kurtosis_enclave_manager_api_bindings diff --git a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_grpc.pb.go b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_grpc.pb.go index 8fc6faa097..cf3a660e8a 100644 --- a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_grpc.pb.go +++ b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.24.3 +// - protoc v4.24.4 // source: kurtosis_enclave_manager_api.proto package kurtosis_enclave_manager_api_bindings diff --git a/enclave-manager/api/scripts/regenerate-proto-bindings.sh b/enclave-manager/api/scripts/regenerate-proto-bindings.sh index a5b107e7a5..7c74cbc9c5 100755 --- a/enclave-manager/api/scripts/regenerate-proto-bindings.sh +++ b/enclave-manager/api/scripts/regenerate-proto-bindings.sh @@ -21,10 +21,31 @@ api_go_mod_rel_file="golang/go.mod" # Typescript api_typescript_rel_dir="typescript" +NODE_ES_TOOLS_PROTOC_BIN_FILENAME="protoc-gen-es" + +NODE_CONNECT_ES_TOOLS_PROTOC_BIN_FILENAME="protoc-gen-connect-es" + # ================================================================================================== # Main Logic # ================================================================================================== +if ! node_es_protoc_bin_filepath="$(which "${NODE_ES_TOOLS_PROTOC_BIN_FILENAME}")"; then + echo "Error: Couldn't find Node gRPC tools protoc plugin binary '${NODE_ES_TOOLS_PROTOC_BIN_FILENAME}' on the PATH" >&2 + return 1 +fi +if [ -z "${node_es_protoc_bin_filepath}" ]; then + echo "Error: Got an empty filepath when looking for the Node gRPC tools protoc plugin binary '${NODE_ES_TOOLS_PROTOC_BIN_FILENAME}'" >&2 + return 1 +fi + +if ! node_connect_es_protoc_bin_filepath="$(which "${NODE_CONNECT_ES_TOOLS_PROTOC_BIN_FILENAME}")"; then + echo "Error: Couldn't find Node gRPC tools protoc plugin binary '${NODE_CONNECT_ES_TOOLS_PROTOC_BIN_FILENAME}' on the PATH" >&2 + return 1 +fi +if [ -z "${node_connect_es_protoc_bin_filepath}" ]; then + echo "Error: Got an empty filepath when looking for the Node gRPC tools protoc plugin binary '${NODE_CONNECT_ES_TOOLS_PROTOC_BIN_FILENAME}'" >&2 + return 1 +fi # Dependencies from other modules api_golang_engine="${repo_root_dirpath}/api/protobuf/engine" api_golang_core="${repo_root_dirpath}/api/protobuf/core" @@ -47,10 +68,10 @@ protoc \ --go-grpc_opt=require_unimplemented_servers=false \ --connect-go_out="${api_golang_proto_generated_abs_dir}" \ --connect-go_opt=module="${api_golang_module}" \ - --plugin=protoc-gen-es="${api_typescript_abs_dir}/node_modules/.bin/protoc-gen-es" \ + --plugin=protoc-gen-es="${node_es_protoc_bin_filepath}" \ --es_out="${api_typescript_abs_dir}/src/" \ --es_opt=target=ts \ - --plugin=protoc-gen-connect-es="${api_typescript_abs_dir}/node_modules/.bin/protoc-gen-connect-es" \ + --plugin=protoc-gen-connect-es="${node_connect_es_protoc_bin_filepath}" \ --connect-es_out="${api_typescript_abs_dir}/src/" \ --connect-es_opt=target=ts \ "${api_proto_abs_dir}"/*.proto \ No newline at end of file diff --git a/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_pb.ts b/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_pb.ts index 34a066ea62..4e975eb423 100644 --- a/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_pb.ts +++ b/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_pb.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v1.3.3 with parameter "target=ts" +// @generated by protoc-gen-es v1.5.1 with parameter "target=ts" // @generated from file kurtosis_enclave_manager_api.proto (package kurtosis_enclave_manager, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/flake.lock b/flake.lock index 54bb92f617..60e9d3d49c 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { @@ -20,16 +20,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1697655685, - "narHash": "sha256-79Kuv+QdgsVc+rkibuAgWHnh8IXrLBTOKg5nM0Qvux0=", + "lastModified": 1706373441, + "narHash": "sha256-S1hbgNbVYhuY2L05OANWqmRzj4cElcbLuIkXTb69xkk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "80c1aab725151632ddc2a20caeb914e76dd0673c", + "rev": "56911ef3403a9318b7621ce745f5452fb9ef6867", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "ref": "nixos-23.11", "repo": "nixpkgs", "type": "github" } @@ -58,11 +58,11 @@ }, "unstable": { "locked": { - "lastModified": 1697723726, - "narHash": "sha256-SaTWPkI8a5xSHX/rrKzUe+/uVNy6zCGMXgoeMb7T9rg=", + "lastModified": 1706371002, + "narHash": "sha256-dwuorKimqSYgyu8Cw6ncKhyQjUDOyuXoxDTVmAXq88s=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7c9cc5a6e5d38010801741ac830a3f8fd667a7a0", + "rev": "c002c6aa977ad22c60398daaa9be52f2203d0006", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index b35be15268..44d25796ac 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "Kurtosis dev flake"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; @@ -13,6 +13,10 @@ let pkgs = nixpkgs.legacyPackages.${system}; unstable_pkgs = unstable.legacyPackages.${system}; + node-devtools = import ./nix-pkgs/node-tools/. { + inherit pkgs system; + nodejs = pkgs.nodejs_20; + }; in { formatter = pkgs.nixpkgs-fmt; @@ -23,11 +27,6 @@ import ./nix-pkgs/openapi-codegen.nix { inherit pkgs; }; grpc-tools-node = import ./nix-pkgs/grpc-tools-node.nix { inherit pkgs; }; - protoc-gen-ts = - import ./nix-pkgs/protoc-gen-ts.nix { inherit pkgs; }; - openapi-typescript = - # import ./nix-pkgs/openapi-typescript.nix { inherit pkgs; }; - import ./nix-pkgs/openapi-ts { inherit pkgs; }; in [ goreleaser go_1_20 @@ -36,6 +35,7 @@ delve enumer nodejs_20 + node2nix yarn protobuf protoc-gen-go @@ -44,7 +44,6 @@ protoc-gen-grpc-web grpc-tools grpcui - openapi-codegen-go rustc cargo rustfmt @@ -53,9 +52,9 @@ libiconv bash-completion # local definition (see above) + openapi-codegen-go grpc-tools-node - protoc-gen-ts - openapi-typescript + node-devtools.nodeDependencies ]; shellHook = '' diff --git a/internal_testsuites/golang/go.mod b/internal_testsuites/golang/go.mod index 5c575e254f..d184091bc1 100644 --- a/internal_testsuites/golang/go.mod +++ b/internal_testsuites/golang/go.mod @@ -21,7 +21,7 @@ require ( ) require ( - github.com/kurtosis-tech/kurtosis/utils v0.0.0-00010101000000-000000000000 + github.com/kurtosis-tech/kurtosis/utils v0.0.0-20240104153602-385833de9d76 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 k8s.io/utils v0.0.0-20230711102312-30195339c3c7 ) diff --git a/nix-pkgs/node-tools/README.md b/nix-pkgs/node-tools/README.md new file mode 100644 index 0000000000..ca3c06e5d8 --- /dev/null +++ b/nix-pkgs/node-tools/README.md @@ -0,0 +1,16 @@ +# NodeJS devtools + +This Nix derivation get all nodeJS needed in the dev environment. + +## Adding/Updating packages + +On this same folder: +```bash +npm install @ +``` +This will update both `package.json` and `package-lock.json` files. + +Then run this command to regenerate to Nix definitions. +```bash +node2nix +``` \ No newline at end of file diff --git a/nix-pkgs/node-tools/default.nix b/nix-pkgs/node-tools/default.nix new file mode 100644 index 0000000000..a720abf3ad --- /dev/null +++ b/nix-pkgs/node-tools/default.nix @@ -0,0 +1,15 @@ +# This file has been generated by node2nix 1.11.1. Do not edit! + +{ pkgs ? import { inherit system; }, system ? builtins.currentSystem +, nodejs ? pkgs."nodejs_14" }: + +let + nodeEnv = import ./node-env.nix { + inherit (pkgs) stdenv lib python2 runCommand writeTextFile writeShellScript; + inherit pkgs nodejs; + libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null; + }; +in import ./node-packages.nix { + inherit (pkgs) fetchurl nix-gitignore stdenv lib fetchgit; + inherit nodeEnv; +} diff --git a/nix-pkgs/node-tools/node-env.nix b/nix-pkgs/node-tools/node-env.nix new file mode 100644 index 0000000000..9df82ba779 --- /dev/null +++ b/nix-pkgs/node-tools/node-env.nix @@ -0,0 +1,684 @@ +# This file originates from node2nix + +{ lib, stdenv, nodejs, python2, pkgs, libtool, runCommand, writeTextFile +, writeShellScript }: + +let + # Workaround to cope with utillinux in Nixpkgs 20.09 and util-linux in Nixpkgs master + utillinux = if pkgs ? utillinux then pkgs.utillinux else pkgs.util-linux; + + python = if nodejs ? python then nodejs.python else python2; + + # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise + tarWrapper = runCommand "tarWrapper" { } '' + mkdir -p $out/bin + + cat > $out/bin/tar <> $out/nix-support/hydra-build-products + ''; + }; + + # Common shell logic + installPackage = writeShellScript "install-package" '' + installPackage() { + local packageName=$1 src=$2 + + local strippedName + + local DIR=$PWD + cd $TMPDIR + + unpackFile $src + + # Make the base dir in which the target dependency resides first + mkdir -p "$(dirname "$DIR/$packageName")" + + if [ -f "$src" ] + then + # Figure out what directory has been unpacked + packageDir="$(find . -maxdepth 1 -type d | tail -1)" + + # Restore write permissions to make building work + find "$packageDir" -type d -exec chmod u+x {} \; + chmod -R u+w "$packageDir" + + # Move the extracted tarball into the output folder + mv "$packageDir" "$DIR/$packageName" + elif [ -d "$src" ] + then + # Get a stripped name (without hash) of the source directory. + # On old nixpkgs it's already set internally. + if [ -z "$strippedName" ] + then + strippedName="$(stripHash $src)" + fi + + # Restore write permissions to make building work + chmod -R u+w "$strippedName" + + # Move the extracted directory into the output folder + mv "$strippedName" "$DIR/$packageName" + fi + + # Change to the package directory to install dependencies + cd "$DIR/$packageName" + } + ''; + + # Bundle the dependencies of the package + # + # Only include dependencies if they don't exist. They may also be bundled in the package. + includeDependencies = { dependencies }: + lib.optionalString (dependencies != [ ]) ('' + mkdir -p node_modules + cd node_modules + '' + (lib.concatMapStrings (dependency: '' + if [ ! -e "${dependency.packageName}" ]; then + ${composePackage dependency} + fi + '') dependencies) + '' + cd .. + ''); + + # Recursively composes the dependencies of a package + composePackage = { name, packageName, src, dependencies ? [ ], ... }@args: + builtins.addErrorContext "while evaluating node package '${packageName}'" '' + installPackage "${packageName}" "${src}" + ${includeDependencies { inherit dependencies; }} + cd .. + ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + ''; + + pinpointDependencies = { dependencies, production }: + let + pinpointDependenciesFromPackageJSON = writeTextFile { + name = "pinpointDependencies.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + function resolveDependencyVersion(location, name) { + if(location == process.env['NIX_STORE']) { + return null; + } else { + var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json"); + + if(fs.existsSync(dependencyPackageJSON)) { + var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON)); + + if(dependencyPackageObj.name == name) { + return dependencyPackageObj.version; + } + } else { + return resolveDependencyVersion(path.resolve(location, ".."), name); + } + } + } + + function replaceDependencies(dependencies) { + if(typeof dependencies == "object" && dependencies !== null) { + for(var dependency in dependencies) { + var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency); + + if(resolvedVersion === null) { + process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n"); + } else { + dependencies[dependency] = resolvedVersion; + } + } + } + } + + /* Read the package.json configuration */ + var packageObj = JSON.parse(fs.readFileSync('./package.json')); + + /* Pinpoint all dependencies */ + replaceDependencies(packageObj.dependencies); + if(process.argv[2] == "development") { + replaceDependencies(packageObj.devDependencies); + } + else { + packageObj.devDependencies = {}; + } + replaceDependencies(packageObj.optionalDependencies); + replaceDependencies(packageObj.peerDependencies); + + /* Write the fixed package.json file */ + fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2)); + ''; + }; + in '' + node ${pinpointDependenciesFromPackageJSON} ${ + if production then "production" else "development" + } + + ${lib.optionalString (dependencies != [ ]) '' + if [ -d node_modules ] + then + cd node_modules + ${ + lib.concatMapStrings + (dependency: pinpointDependenciesOfPackage dependency) + dependencies + } + cd .. + fi + ''} + ''; + + # Recursively traverses all dependencies of a package and pinpoints all + # dependencies in the package.json file to the versions that are actually + # being used. + + pinpointDependenciesOfPackage = + { packageName, dependencies ? [ ], production ? true, ... }@args: '' + if [ -d "${packageName}" ] + then + cd "${packageName}" + ${pinpointDependencies { inherit dependencies production; }} + cd .. + ${ + lib.optionalString (builtins.substring 0 1 packageName == "@") + "cd .." + } + fi + ''; + + # Extract the Node.js source code which is used to compile packages with + # native bindings + nodeSources = runCommand "node-sources" { } '' + tar --no-same-owner --no-same-permissions -xf ${nodejs.src} + mv node-* $out + ''; + + # Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty) + addIntegrityFieldsScript = writeTextFile { + name = "addintegrityfields.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + function augmentDependencies(baseDir, dependencies) { + for(var dependencyName in dependencies) { + var dependency = dependencies[dependencyName]; + + // Open package.json and augment metadata fields + var packageJSONDir = path.join(baseDir, "node_modules", dependencyName); + var packageJSONPath = path.join(packageJSONDir, "package.json"); + + if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored + console.log("Adding metadata fields to: "+packageJSONPath); + var packageObj = JSON.parse(fs.readFileSync(packageJSONPath)); + + if(dependency.integrity) { + packageObj["_integrity"] = dependency.integrity; + } else { + packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads. + } + + if(dependency.resolved) { + packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided + } else { + packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories. + } + + if(dependency.from !== undefined) { // Adopt from property if one has been provided + packageObj["_from"] = dependency.from; + } + + fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2)); + } + + // Augment transitive dependencies + if(dependency.dependencies !== undefined) { + augmentDependencies(packageJSONDir, dependency.dependencies); + } + } + } + + if(fs.existsSync("./package-lock.json")) { + var packageLock = JSON.parse(fs.readFileSync("./package-lock.json")); + + if(![1, 2].includes(packageLock.lockfileVersion)) { + process.stderr.write("Sorry, I only understand lock file versions 1 and 2!\n"); + process.exit(1); + } + + if(packageLock.dependencies !== undefined) { + augmentDependencies(".", packageLock.dependencies); + } + } + ''; + }; + + # Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes + reconstructPackageLock = writeTextFile { + name = "reconstructpackagelock.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + var packageObj = JSON.parse(fs.readFileSync("package.json")); + + var lockObj = { + name: packageObj.name, + version: packageObj.version, + lockfileVersion: 2, + requires: true, + packages: { + "": { + name: packageObj.name, + version: packageObj.version, + license: packageObj.license, + bin: packageObj.bin, + dependencies: packageObj.dependencies, + engines: packageObj.engines, + optionalDependencies: packageObj.optionalDependencies + } + }, + dependencies: {} + }; + + function augmentPackageJSON(filePath, packages, dependencies) { + var packageJSON = path.join(filePath, "package.json"); + if(fs.existsSync(packageJSON)) { + var packageObj = JSON.parse(fs.readFileSync(packageJSON)); + packages[filePath] = { + version: packageObj.version, + integrity: "sha1-000000000000000000000000000=", + dependencies: packageObj.dependencies, + engines: packageObj.engines, + optionalDependencies: packageObj.optionalDependencies + }; + dependencies[packageObj.name] = { + version: packageObj.version, + integrity: "sha1-000000000000000000000000000=", + dependencies: {} + }; + processDependencies(path.join(filePath, "node_modules"), packages, dependencies[packageObj.name].dependencies); + } + } + + function processDependencies(dir, packages, dependencies) { + if(fs.existsSync(dir)) { + var files = fs.readdirSync(dir); + + files.forEach(function(entry) { + var filePath = path.join(dir, entry); + var stats = fs.statSync(filePath); + + if(stats.isDirectory()) { + if(entry.substr(0, 1) == "@") { + // When we encounter a namespace folder, augment all packages belonging to the scope + var pkgFiles = fs.readdirSync(filePath); + + pkgFiles.forEach(function(entry) { + if(stats.isDirectory()) { + var pkgFilePath = path.join(filePath, entry); + augmentPackageJSON(pkgFilePath, packages, dependencies); + } + }); + } else { + augmentPackageJSON(filePath, packages, dependencies); + } + } + }); + } + } + + processDependencies("node_modules", lockObj.packages, lockObj.dependencies); + + fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2)); + ''; + }; + + # Script that links bins defined in package.json to the node_modules bin directory + # NPM does not do this for top-level packages itself anymore as of v7 + linkBinsScript = writeTextFile { + name = "linkbins.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + var packageObj = JSON.parse(fs.readFileSync("package.json")); + + var nodeModules = Array(packageObj.name.split("/").length).fill("..").join(path.sep); + + if(packageObj.bin !== undefined) { + fs.mkdirSync(path.join(nodeModules, ".bin")) + + if(typeof packageObj.bin == "object") { + Object.keys(packageObj.bin).forEach(function(exe) { + if(fs.existsSync(packageObj.bin[exe])) { + console.log("linking bin '" + exe + "'"); + fs.symlinkSync( + path.join("..", packageObj.name, packageObj.bin[exe]), + path.join(nodeModules, ".bin", exe) + ); + } + else { + console.log("skipping non-existent bin '" + exe + "'"); + } + }) + } + else { + if(fs.existsSync(packageObj.bin)) { + console.log("linking bin '" + packageObj.bin + "'"); + fs.symlinkSync( + path.join("..", packageObj.name, packageObj.bin), + path.join(nodeModules, ".bin", packageObj.name.split("/").pop()) + ); + } + else { + console.log("skipping non-existent bin '" + packageObj.bin + "'"); + } + } + } + else if(packageObj.directories !== undefined && packageObj.directories.bin !== undefined) { + fs.mkdirSync(path.join(nodeModules, ".bin")) + + fs.readdirSync(packageObj.directories.bin).forEach(function(exe) { + if(fs.existsSync(path.join(packageObj.directories.bin, exe))) { + console.log("linking bin '" + exe + "'"); + fs.symlinkSync( + path.join("..", packageObj.name, packageObj.directories.bin, exe), + path.join(nodeModules, ".bin", exe) + ); + } + else { + console.log("skipping non-existent bin '" + exe + "'"); + } + }) + } + ''; + }; + + prepareAndInvokeNPM = + { packageName, bypassCache, reconstructLock, npmFlags, production }: + let + forceOfflineFlag = if bypassCache then + "--offline" + else + "--registry http://www.example.com"; + in '' + # Pinpoint the versions of all dependencies to the ones that are actually being used + echo "pinpointing versions of dependencies..." + source $pinpointDependenciesScriptPath + + # Patch the shebangs of the bundled modules to prevent them from + # calling executables outside the Nix store as much as possible + patchShebangs . + + # Deploy the Node.js package by running npm install. Since the + # dependencies have been provided already by ourselves, it should not + # attempt to install them again, which is good, because we want to make + # it Nix's responsibility. If it needs to install any dependencies + # anyway (e.g. because the dependency parameters are + # incomplete/incorrect), it fails. + # + # The other responsibilities of NPM are kept -- version checks, build + # steps, postprocessing etc. + + export HOME=$TMPDIR + cd "${packageName}" + runHook preRebuild + + ${lib.optionalString bypassCache '' + ${lib.optionalString reconstructLock '' + if [ -f package-lock.json ] + then + echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!" + echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!" + rm package-lock.json + else + echo "No package-lock.json file found, reconstructing..." + fi + + node ${reconstructPackageLock} + ''} + + node ${addIntegrityFieldsScript} + ''} + + npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${ + lib.optionalString production "--production" + } rebuild + + runHook postRebuild + + if [ "''${dontNpmInstall-}" != "1" ] + then + # NPM tries to download packages even when they already exist if npm-shrinkwrap is used. + rm -f npm-shrinkwrap.json + + npm ${forceOfflineFlag} --nodedir=${nodeSources} --no-bin-links --ignore-scripts ${npmFlags} ${ + lib.optionalString production "--production" + } install + fi + + # Link executables defined in package.json + node ${linkBinsScript} + ''; + + # Builds and composes an NPM package including all its dependencies + buildNodePackage = { name, packageName, version ? null, dependencies ? [ ] + , buildInputs ? [ ], production ? true, npmFlags ? "" + , dontNpmInstall ? false, bypassCache ? false, reconstructLock ? false + , preRebuild ? "", dontStrip ? true, unpackPhase ? "true" + , buildPhase ? "true", meta ? { }, ... }@args: + + let + extraArgs = removeAttrs args [ + "name" + "dependencies" + "buildInputs" + "dontStrip" + "dontNpmInstall" + "preRebuild" + "unpackPhase" + "buildPhase" + "meta" + ]; + in stdenv.mkDerivation ({ + name = "${name}${if version == null then "" else "-${version}"}"; + buildInputs = [ tarWrapper python nodejs ] + ++ lib.optional (stdenv.isLinux) utillinux + ++ lib.optional (stdenv.isDarwin) libtool ++ buildInputs; + + inherit nodejs; + + inherit + dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall preRebuild unpackPhase buildPhase; + + compositionScript = composePackage args; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; + + passAsFile = [ "compositionScript" "pinpointDependenciesScript" ]; + + installPhase = '' + source ${installPackage} + + # Create and enter a root node_modules/ folder + mkdir -p $out/lib/node_modules + cd $out/lib/node_modules + + # Compose the package and all its dependencies + source $compositionScriptPath + + ${prepareAndInvokeNPM { + inherit packageName bypassCache reconstructLock npmFlags production; + }} + + # Create symlink to the deployed executable folder, if applicable + if [ -d "$out/lib/node_modules/.bin" ] + then + ln -s $out/lib/node_modules/.bin $out/bin + + # Fixup all executables + ls $out/bin/* | while read i + do + file="$(readlink -f "$i")" + chmod u+rwx "$file" + if isScript "$file" + then + sed -i 's/\r$//' "$file" # convert crlf to lf + fi + done + fi + + # Create symlinks to the deployed manual page folders, if applicable + if [ -d "$out/lib/node_modules/${packageName}/man" ] + then + mkdir -p $out/share + for dir in "$out/lib/node_modules/${packageName}/man/"* + do + mkdir -p $out/share/man/$(basename "$dir") + for page in "$dir"/* + do + ln -s $page $out/share/man/$(basename "$dir") + done + done + fi + + # Run post install hook, if provided + runHook postInstall + ''; + + meta = { + # default to Node.js' platforms + platforms = nodejs.meta.platforms; + } // meta; + } // extraArgs); + + # Builds a node environment (a node_modules folder and a set of binaries) + buildNodeDependencies = { name, packageName, version ? null, src + , dependencies ? [ ], buildInputs ? [ ], production ? true, npmFlags ? "" + , dontNpmInstall ? false, bypassCache ? false, reconstructLock ? false + , dontStrip ? true, unpackPhase ? "true", buildPhase ? "true", ... }@args: + + let extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ]; + in stdenv.mkDerivation ({ + name = "node-dependencies-${name}${ + if version == null then "" else "-${version}" + }"; + + buildInputs = [ tarWrapper python nodejs ] + ++ lib.optional (stdenv.isLinux) utillinux + ++ lib.optional (stdenv.isDarwin) libtool ++ buildInputs; + + inherit + dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall unpackPhase buildPhase; + + includeScript = includeDependencies { inherit dependencies; }; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; + + passAsFile = [ "includeScript" "pinpointDependenciesScript" ]; + + installPhase = '' + source ${installPackage} + + mkdir -p $out/${packageName} + cd $out/${packageName} + + source $includeScriptPath + + # Create fake package.json to make the npm commands work properly + cp ${src}/package.json . + chmod 644 package.json + ${lib.optionalString bypassCache '' + if [ -f ${src}/package-lock.json ] + then + cp ${src}/package-lock.json . + chmod 644 package-lock.json + fi + ''} + + # Go to the parent folder to make sure that all packages are pinpointed + cd .. + ${lib.optionalString (builtins.substring 0 1 packageName == "@") + "cd .."} + + ${prepareAndInvokeNPM { + inherit packageName bypassCache reconstructLock npmFlags production; + }} + + # Expose the executables that were installed + cd .. + ${lib.optionalString (builtins.substring 0 1 packageName == "@") + "cd .."} + + mv ${packageName} lib + ln -s $out/lib/node_modules/.bin $out/bin + ''; + } // extraArgs); + + # Builds a development shell + buildNodeShell = { name, packageName, version ? null, src, dependencies ? [ ] + , buildInputs ? [ ], production ? true, npmFlags ? "" + , dontNpmInstall ? false, bypassCache ? false, reconstructLock ? false + , dontStrip ? true, unpackPhase ? "true", buildPhase ? "true", ... }@args: + + let + nodeDependencies = buildNodeDependencies args; + extraArgs = removeAttrs args [ + "name" + "dependencies" + "buildInputs" + "dontStrip" + "dontNpmInstall" + "unpackPhase" + "buildPhase" + ]; + in stdenv.mkDerivation ({ + name = + "node-shell-${name}${if version == null then "" else "-${version}"}"; + + buildInputs = [ python nodejs ] ++ lib.optional (stdenv.isLinux) utillinux + ++ buildInputs; + buildCommand = '' + mkdir -p $out/bin + cat > $out/bin/shell <=14" }, - "devDependencies": { - "openapi-typescript": "^7.0.0-next.5" + "peerDependencies": { + "@bufbuild/protobuf": "1.5.1" + }, + "peerDependenciesMeta": { + "@bufbuild/protobuf": { + "optional": true + } + } + }, + "node_modules/@bufbuild/protoc-gen-es/node_modules/@bufbuild/protobuf": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.5.1.tgz", + "integrity": "sha512-LX+MeB1AzlbqgJXkq83lilQpLGnPvsAMj7SH8KtJAmQfBc55ee78Stxuff/HMw0xLMYJN3P1FBh5TENgjJof1w==" + }, + "node_modules/@bufbuild/protoc-gen-es/node_modules/@bufbuild/protoplugin": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.5.1.tgz", + "integrity": "sha512-4qQD3UIEXflPYCEPZxyvi9yoQiX3ONWgLw24uLJrw9AnbY7Pw1xT5v8yIMXIVccBEZNSpvIF6qD/JXfIapjRtw==", + "dependencies": { + "@bufbuild/protobuf": "1.5.1", + "@typescript/vfs": "^1.4.0", + "typescript": "4.5.2" + } + }, + "node_modules/@bufbuild/protoplugin": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.7.1.tgz", + "integrity": "sha512-bnPFXs38IXjL2EdpkthkCa/+SXOxERnXyV///rQj1wyidJmw21wOvqpucuIh25YnPtdrUItcIFFDVCoKPkuCPQ==", + "dependencies": { + "@bufbuild/protobuf": "1.7.1", + "@typescript/vfs": "^1.4.0", + "typescript": "4.5.2" + } + }, + "node_modules/@connectrpc/protoc-gen-connect-es": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@connectrpc/protoc-gen-connect-es/-/protoc-gen-connect-es-0.13.2.tgz", + "integrity": "sha512-XGV6TN1y36g6RVDzn+YPDE4Q3QCaobwPNo/N93QXEeql/qI4ROTNbIyl0EfAPlsXITMG3k9WBOg49+rF21mMbg==", + "dependencies": { + "@bufbuild/protobuf": "^1.2.1", + "@bufbuild/protoplugin": "^1.2.1" + }, + "bin": { + "protoc-gen-connect-es": "bin/protoc-gen-connect-es" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@bufbuild/protoc-gen-es": "^1.2.1", + "@connectrpc/connect": "0.13.2" + }, + "peerDependenciesMeta": { + "@bufbuild/protoc-gen-es": { + "optional": true + }, + "@connectrpc/connect": { + "optional": true + } } }, "node_modules/@redocly/ajv": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -28,13 +111,11 @@ } }, "node_modules/@redocly/openapi-core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.5.0.tgz", - "integrity": "sha512-AnDLoDl1+a7mZO4+lx0KG8zH04BQx4ez6yh403PuNl9/0ygbicPPc9QG/y0/0OImChOA+knKLpJazNFjzhOAeg==", - "dev": true, + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.8.1.tgz", + "integrity": "sha512-8gTDKZaGarknXiY/yYUx0cxnO8GEBWIG+H0LT+G/MWkBtOKL3p4cmEhjOC78klSeqjwEddzqCBZw1Ap7rLH0ww==", "dependencies": { "@redocly/ajv": "^8.11.0", - "@types/node": "^14.11.8", "colorette": "^1.2.0", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", @@ -49,17 +130,18 @@ "npm": ">=7.0.0" } }, - "node_modules/@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", - "dev": true + "node_modules/@typescript/vfs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz", + "integrity": "sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==", + "dependencies": { + "debug": "^4.1.1" + } }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, "engines": { "node": ">=6" } @@ -67,20 +149,17 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -88,20 +167,38 @@ "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/google-protobuf": { + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", + "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" }, "node_modules/js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -110,7 +207,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -121,20 +217,17 @@ "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "dev": true + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" }, "node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -142,11 +235,15 @@ "node": ">=10" } }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -174,7 +271,6 @@ "version": "7.0.0-next.5", "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.0.0-next.5.tgz", "integrity": "sha512-zqEDw/FZkT0ndOCd8EybkDVwEYgaOh+ryWm6OCON70DmY9YqUnNSIVyRFVjN8hesa0bxOs9QOMzXAasczNdHbQ==", - "dev": true, "dependencies": { "@redocly/openapi-core": "^1.4.1", "ansi-colors": "^4.1.3", @@ -191,11 +287,22 @@ "resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.5.tgz", "integrity": "sha512-MRffg93t0hgGZbYTxg60hkRIK2sRuEOHEtCUgMuLgbCC33TMQ68AmxskzUlauzZYD47+ENeGV/ElI7qnWqrAxA==" }, + "node_modules/openapi-typescript/node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, "engines": { "node": ">=4" } @@ -204,7 +311,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -213,7 +319,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -222,7 +327,6 @@ "version": "9.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", - "dev": true, "engines": { "node": ">=12" }, @@ -233,27 +337,35 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-protoc-gen": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.15.0.tgz", + "integrity": "sha512-TycnzEyrdVDlATJ3bWFTtra3SCiEP0W0vySXReAuEygXCUr1j2uaVyL0DhzjwuUdQoW5oXPwk6oZWeA0955V+g==", + "dependencies": { + "google-protobuf": "^3.15.5" + }, + "bin": { + "protoc-gen-ts": "bin/protoc-gen-ts" + } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -261,14 +373,12 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -277,14 +387,12 @@ "node_modules/yaml-ast-parser": { "version": "0.0.43", "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", - "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", - "dev": true + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==" }, "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "engines": { "node": ">=12" } diff --git a/nix-pkgs/node-tools/package.json b/nix-pkgs/node-tools/package.json new file mode 100644 index 0000000000..242e54774c --- /dev/null +++ b/nix-pkgs/node-tools/package.json @@ -0,0 +1,12 @@ +{ + "name": "kurtosis-node-tools", + "version": "1.0.0", + "description": "NodeJS dev tools used in the development", + "dependencies": { + "@bufbuild/protoc-gen-es": "1.5.1", + "@connectrpc/protoc-gen-connect-es": "0.13.2", + "ts-protoc-gen": "0.15.0", + "openapi-typescript": "7.0.0-next.5", + "openapi-fetch": "0.8.2" + } +} diff --git a/nix-pkgs/openapi-ts/default.nix b/nix-pkgs/openapi-ts/default.nix deleted file mode 100644 index dbacf8a77f..0000000000 --- a/nix-pkgs/openapi-ts/default.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ pkgs ? import { } }: -with pkgs; -let - pname = "openapi-typescript"; - nodejs = pkgs.nodejs_18; - - openapi-ts = pkgs.buildNpmPackage { - name = "${pname}_node_modules"; - - # The packages required by the build process - buildInputs = [ nodejs ]; - dontNpmBuild = true; - - # The code sources for the package - src = ./.; - npmDepsHash = "sha256-yZcriNNQnin0IHHypPq46gdMzdT5j3J2cs40NVldfwY="; - - # How the output of the build phase - installPhase = '' - mkdir $out - cp -r node_modules/ $out - ''; - }; - - openapi-ts-bin = pkgs.writeScript "${pname}_wrapper" '' - #! ${pkgs.stdenv.shell} - ${nodejs}/bin/npm exec --prefix ${openapi-ts}/node_modules -- openapi-typescript "$@" - ''; - -in stdenv.mkDerivation { - name = pname; - src = ./.; - installPhase = '' - mkdir -p $out/bin - cp -r ${openapi-ts-bin} $out/bin/openapi-typescript - ''; -} diff --git a/nix-pkgs/openapi-ts/package.json b/nix-pkgs/openapi-ts/package.json deleted file mode 100644 index 7ba612bf00..0000000000 --- a/nix-pkgs/openapi-ts/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "devDependencies": { - "openapi-typescript": "^7.0.0-next.5" - }, - "dependencies": { - "openapi-fetch": "^0.8.2" - } -} diff --git a/nix-pkgs/protoc-gen-ts.nix b/nix-pkgs/protoc-gen-ts.nix deleted file mode 100644 index 8801d821a8..0000000000 --- a/nix-pkgs/protoc-gen-ts.nix +++ /dev/null @@ -1,148 +0,0 @@ -{ pkgs ? import { } }: -with pkgs; -let - pkg_name = "protoc-gen-ts"; - pkg_version = "0.8.7"; - - # Using fixed output derivation so we can fetch things from outside - # and to avoid messing with Bazel's hermetic deps (mostly nodejs) - # we'll build it direclty using using Nix's nodejs. Very very hack solution - # after countless hours fighting Bazel deps vs nix sandbox. Maybe there a way easer - # way to tell bazel to use external nodejs but I couldn't figure it out. - # It seems the upstream repo is moving away from Bazel into Cargo. - deps = stdenv.mkDerivation rec { - name = "${pkg_name}-build"; - - src = fetchFromGitHub { - owner = "thesayyn"; - repo = pkg_name; - rev = pkg_version; - hash = "sha256-PGprtSPMRTodt/SD6gpEr/n22jiNqB1/C6HJGlDndLg="; - }; - - buildInputs = [ git cacert nodejs ]; - - # Setup TS/Node config files directly into the src repo - tsconfig_json = pkgs.writeText "tsconfig_json" '' - { - "compilerOptions": { - "target": "ES2020", - "module": "EsNext", - "moduleResolution": "node", - "outDir": "./.build", - "noImplicitAny": true - } - } - ''; - - package_json = pkgs.writeText "package_json" '' - { - "name": "example-node-nix", - "version": "1.0.0", - "main": "dist/index.js", - "bin": { - "example-node-nix": "dist/index.js" - }, - "scripts": { - "build": "tsc -p tsconfig.json && rollup -c rollup.config.js", - "start": "node dist/index.js" - }, - "dependencies": { - "@grpc/grpc-js": "^1.7.3", - "@types/google-protobuf": "^3.15.5", - "google-protobuf": "^3.19.1" - }, - "devDependencies": { - "@rollup/plugin-commonjs": "^23.0.2", - "@rollup/plugin-node-resolve": "^15.0.1", - "@rollup/plugin-typescript": "^11.1.5", - "@tsconfig/node16": "^1.0.1", - "@tsconfig/node16-strictest": "^1.0.0", - "@types/node": "^18.7.14", - "rollup": "<3.0.0", - "typescript": "^4.8.2", - "tslib": "^2.6.2" - } - } - ''; - - rollup_config_js = pkgs.writeText "rollup_config_js" '' - import nodeResolve from '@rollup/plugin-node-resolve'; - import commonjs from '@rollup/plugin-commonjs'; - import typescript from '@rollup/plugin-typescript'; - import fs from "node:fs"; - - const executable = () => { - return { - name: 'executable', - writeBundle: (options) => { - fs.chmodSync(options.file, '755'); - }, - }; - }; - - export default { - input: "./index.ts", - output: { - file: '.bin/protoc-gen-ts.js', - format: 'cjs' - }, - plugins: [ - typescript(), - nodeResolve(), - commonjs(), - executable() - ], - onwarn(message, warn) { - if (message.code === "EVAL" || message.code == "THIS_IS_UNDEFINED") { - return; - } - warn(message); - } - } - ''; - - buildPhase = '' - cp ${package_json} src/package.json - cp ${rollup_config_js} src/rollup.config.js - cp ${tsconfig_json} src/tsconfig.json - cd src - - echo ">> Create .tmp folder as Nix sandbox are homeless and nodejs needs one" - mkdir -p .tmp - export HOME=$(pwd)/.tmp - - echo ">> Intsalling TS/JS/nodejs deps" - npm install - - echo ">> Patching npm's TS and RollUp scripts to work on Nix" - patchShebangs --build -- node_modules/typescript/bin/tsc - patchShebangs --build -- node_modules/rollup/dist/bin/rollup - - echo ">> Building" - npm run build - ''; - - installPhase = '' - mkdir -p $out/bin - cp .bin/protoc-gen-ts.js $out/bin/protoc-gen-ts - ''; - - outputHashAlgo = "sha256"; - outputHashMode = "recursive"; - outputHash = "sha256-cIIFpEXlg/UogK4QskIeWFDuXReFDBKokddTB6juTZo="; - }; - -in stdenv.mkDerivation rec { - name = pkg_name; - version = pkg_version; - phases = [ "installPhase" ]; - - # Fixed Output derivation can't have Nix references (paths /nix/...) on it - # and we need to add it on this second (and normal) derivation. We're adding - # a shebang to call nodejs in the compiled script. - installPhase = '' - mkdir -p $out/bin - echo "$(echo '#!${nodejs}/bin/node' && cat '${deps}/bin/protoc-gen-ts')" > $out/bin/protoc-gen-ts - ''; -} From 1eeb3eb3880e8df83c78642dce391070cda9f515 Mon Sep 17 00:00:00 2001 From: Kevin Today Date: Wed, 31 Jan 2024 18:06:52 -0300 Subject: [PATCH 019/102] feat: Add RunStarlarkScript to enclave manager API (#2103) ## Description: @Dartoxian is going to be running Starlark scripts generated by the Enclave Manager frontend, and ## Is this change user facing? NO Co-authored-by: mieubrisse --- .../kurtosis_enclave_manager_api.pb.go | 447 +++++++++++------- .../kurtosis_enclave_manager_api.connect.go | 27 ++ .../kurtosis_enclave_manager_api_grpc.pb.go | 66 ++- .../kurtosis_enclave_manager_api.proto | 7 + .../kurtosis_enclave_manager_api_connect.ts | 11 +- .../src/kurtosis_enclave_manager_api_pb.ts | 51 +- enclave-manager/server/server.go | 45 +- 7 files changed, 470 insertions(+), 184 deletions(-) diff --git a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api.pb.go b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api.pb.go index 99288ac7ee..0fb7382651 100644 --- a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api.pb.go +++ b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api.pb.go @@ -342,6 +342,69 @@ func (x *RunStarlarkPackageRequest) GetRunStarlarkPackageArgs() *kurtosis_core_r return nil } +type RunStarlarkScriptRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ApicIpAddress string `protobuf:"bytes,1,opt,name=apic_ip_address,json=apicIpAddress,proto3" json:"apic_ip_address,omitempty"` + ApicPort int32 `protobuf:"varint,2,opt,name=apic_port,json=apicPort,proto3" json:"apic_port,omitempty"` + RunStarlarkScriptArgs *kurtosis_core_rpc_api_bindings.RunStarlarkScriptArgs `protobuf:"bytes,3,opt,name=RunStarlarkScriptArgs,proto3" json:"RunStarlarkScriptArgs,omitempty"` +} + +func (x *RunStarlarkScriptRequest) Reset() { + *x = RunStarlarkScriptRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunStarlarkScriptRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunStarlarkScriptRequest) ProtoMessage() {} + +func (x *RunStarlarkScriptRequest) ProtoReflect() protoreflect.Message { + mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunStarlarkScriptRequest.ProtoReflect.Descriptor instead. +func (*RunStarlarkScriptRequest) Descriptor() ([]byte, []int) { + return file_kurtosis_enclave_manager_api_proto_rawDescGZIP(), []int{5} +} + +func (x *RunStarlarkScriptRequest) GetApicIpAddress() string { + if x != nil { + return x.ApicIpAddress + } + return "" +} + +func (x *RunStarlarkScriptRequest) GetApicPort() int32 { + if x != nil { + return x.ApicPort + } + return 0 +} + +func (x *RunStarlarkScriptRequest) GetRunStarlarkScriptArgs() *kurtosis_core_rpc_api_bindings.RunStarlarkScriptArgs { + if x != nil { + return x.RunStarlarkScriptArgs + } + return nil +} + type InspectFilesArtifactContentsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -355,7 +418,7 @@ type InspectFilesArtifactContentsRequest struct { func (x *InspectFilesArtifactContentsRequest) Reset() { *x = InspectFilesArtifactContentsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[5] + mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -368,7 +431,7 @@ func (x *InspectFilesArtifactContentsRequest) String() string { func (*InspectFilesArtifactContentsRequest) ProtoMessage() {} func (x *InspectFilesArtifactContentsRequest) ProtoReflect() protoreflect.Message { - mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[5] + mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -381,7 +444,7 @@ func (x *InspectFilesArtifactContentsRequest) ProtoReflect() protoreflect.Messag // Deprecated: Use InspectFilesArtifactContentsRequest.ProtoReflect.Descriptor instead. func (*InspectFilesArtifactContentsRequest) Descriptor() ([]byte, []int) { - return file_kurtosis_enclave_manager_api_proto_rawDescGZIP(), []int{5} + return file_kurtosis_enclave_manager_api_proto_rawDescGZIP(), []int{6} } func (x *InspectFilesArtifactContentsRequest) GetApicIpAddress() string { @@ -418,7 +481,7 @@ type DownloadFilesArtifactRequest struct { func (x *DownloadFilesArtifactRequest) Reset() { *x = DownloadFilesArtifactRequest{} if protoimpl.UnsafeEnabled { - mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[6] + mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -431,7 +494,7 @@ func (x *DownloadFilesArtifactRequest) String() string { func (*DownloadFilesArtifactRequest) ProtoMessage() {} func (x *DownloadFilesArtifactRequest) ProtoReflect() protoreflect.Message { - mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[6] + mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -444,7 +507,7 @@ func (x *DownloadFilesArtifactRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DownloadFilesArtifactRequest.ProtoReflect.Descriptor instead. func (*DownloadFilesArtifactRequest) Descriptor() ([]byte, []int) { - return file_kurtosis_enclave_manager_api_proto_rawDescGZIP(), []int{6} + return file_kurtosis_enclave_manager_api_proto_rawDescGZIP(), []int{7} } func (x *DownloadFilesArtifactRequest) GetApicIpAddress() string { @@ -480,7 +543,7 @@ type GetStarlarkRunRequest struct { func (x *GetStarlarkRunRequest) Reset() { *x = GetStarlarkRunRequest{} if protoimpl.UnsafeEnabled { - mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[7] + mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -493,7 +556,7 @@ func (x *GetStarlarkRunRequest) String() string { func (*GetStarlarkRunRequest) ProtoMessage() {} func (x *GetStarlarkRunRequest) ProtoReflect() protoreflect.Message { - mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[7] + mi := &file_kurtosis_enclave_manager_api_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -506,7 +569,7 @@ func (x *GetStarlarkRunRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetStarlarkRunRequest.ProtoReflect.Descriptor instead. func (*GetStarlarkRunRequest) Descriptor() ([]byte, []int) { - return file_kurtosis_enclave_manager_api_proto_rawDescGZIP(), []int{7} + return file_kurtosis_enclave_manager_api_proto_rawDescGZIP(), []int{8} } func (x *GetStarlarkRunRequest) GetApicIpAddress() string { @@ -574,124 +637,143 @@ var file_kurtosis_enclave_manager_api_proto_rawDesc = []byte{ 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x41, 0x72, 0x67, 0x73, 0x52, 0x16, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, - 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x41, 0x72, 0x67, 0x73, 0x22, 0xc6, - 0x01, 0x0a, 0x23, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, - 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x69, - 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x61, 0x70, 0x69, 0x63, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, - 0x0a, 0x09, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x08, 0x61, 0x70, 0x69, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x5a, 0x0a, 0x13, 0x66, - 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, 0x75, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x6c, - 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, - 0x64, 0x55, 0x75, 0x69, 0x64, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0xd4, 0x01, 0x0a, 0x1c, 0x44, 0x6f, 0x77, 0x6e, - 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x70, 0x69, 0x63, + 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x41, 0x72, 0x67, 0x73, 0x22, 0xbf, + 0x01, 0x0a, 0x18, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61, + 0x70, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x70, 0x69, 0x63, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x61, 0x70, 0x69, 0x63, 0x50, 0x6f, 0x72, 0x74, + 0x12, 0x5e, 0x0a, 0x15, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x28, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x52, 0x15, 0x52, 0x75, 0x6e, 0x53, 0x74, + 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, + 0x22, 0xc6, 0x01, 0x0a, 0x23, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, + 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x70, 0x69, 0x63, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x08, 0x61, 0x70, 0x69, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x6f, 0x0a, - 0x1d, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, - 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, - 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x41, 0x72, - 0x67, 0x73, 0x52, 0x1a, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, - 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x41, 0x72, 0x67, 0x73, 0x22, 0x5c, - 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x70, 0x69, 0x63, 0x5f, - 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x61, 0x70, 0x69, 0x63, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x08, 0x61, 0x70, 0x69, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x32, 0xd5, 0x09, 0x0a, - 0x1c, 0x4b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, - 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x64, 0x0a, - 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2c, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, + 0x01, 0x28, 0x05, 0x52, 0x08, 0x61, 0x70, 0x69, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x5a, 0x0a, + 0x13, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, + 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0xd4, 0x01, 0x0a, 0x1c, 0x44, 0x6f, + 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, + 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x70, + 0x69, 0x63, 0x5f, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x70, 0x69, 0x63, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x61, 0x70, 0x69, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, + 0x6f, 0x0a, 0x1d, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x5f, 0x61, 0x72, 0x67, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, + 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, + 0x41, 0x72, 0x67, 0x73, 0x52, 0x1a, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x41, 0x72, 0x67, 0x73, + 0x22, 0x5c, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, + 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x70, 0x69, + 0x63, 0x5f, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x61, 0x70, 0x69, 0x63, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x61, 0x70, 0x69, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x32, 0xce, + 0x0a, 0x0a, 0x1c, 0x4b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, + 0x64, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2c, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, + 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, + 0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, + 0x61, 0x76, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, + 0x6c, 0x61, 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x65, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2c, + 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, + 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, + 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4c, 0x6f, 0x67, 0x73, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x22, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, + 0x12, 0xa1, 0x01, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, + 0x69, 0x64, 0x73, 0x12, 0x42, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, + 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x47, + 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, + 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x79, 0x0a, 0x12, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, + 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x33, 0x2e, 0x6b, 0x75, 0x72, + 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, + 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, + 0x77, 0x0a, 0x11, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x12, 0x32, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, - 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, - 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, 0x61, - 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, - 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x6b, - 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, - 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, - 0x67, 0x73, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x22, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, - 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, - 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0xa1, - 0x01, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, - 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, - 0x73, 0x12, 0x42, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, - 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, - 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x79, 0x0a, 0x12, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, - 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x33, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, - 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x72, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, - 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, + 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, + 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x1d, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, + 0x6c, 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x21, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, + 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x98, 0x01, + 0x0a, 0x1c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3d, + 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, + 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, + 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, - 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x53, 0x0a, - 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x1d, - 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x21, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x98, 0x01, 0x0a, 0x1c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x3d, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, - 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x49, - 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, - 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x79, 0x0a, - 0x15, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, - 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x36, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, - 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x72, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, - 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, - 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, - 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x4a, 0x0a, 0x0e, 0x44, 0x65, 0x73, 0x74, - 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x1e, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, - 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, - 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x12, 0x2f, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, - 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x72, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, - 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x42, 0x64, 0x5a, 0x62, 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, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, - 0x65, 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, - 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, - 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x61, 0x70, - 0x69, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x79, 0x0a, 0x15, 0x44, 0x6f, 0x77, 0x6e, + 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x12, 0x36, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, + 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x77, + 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, + 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x4a, 0x0a, 0x0e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, + 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x1e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, + 0x70, 0x69, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, + 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, + 0x6e, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, + 0x6e, 0x12, 0x2f, 0x2e, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, + 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, + 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x64, 0x5a, 0x62, 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, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x2d, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, + 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, + 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x5f, 0x62, 0x69, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -707,7 +789,7 @@ func file_kurtosis_enclave_manager_api_proto_rawDescGZIP() []byte { } var file_kurtosis_enclave_manager_api_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_kurtosis_enclave_manager_api_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_kurtosis_enclave_manager_api_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_kurtosis_enclave_manager_api_proto_goTypes = []interface{}{ (HealthCheckResponse_ServingStatus)(0), // 0: kurtosis_enclave_manager.HealthCheckResponse.ServingStatus (*HealthCheckRequest)(nil), // 1: kurtosis_enclave_manager.HealthCheckRequest @@ -715,58 +797,63 @@ var file_kurtosis_enclave_manager_api_proto_goTypes = []interface{}{ (*GetServicesRequest)(nil), // 3: kurtosis_enclave_manager.GetServicesRequest (*GetListFilesArtifactNamesAndUuidsRequest)(nil), // 4: kurtosis_enclave_manager.GetListFilesArtifactNamesAndUuidsRequest (*RunStarlarkPackageRequest)(nil), // 5: kurtosis_enclave_manager.RunStarlarkPackageRequest - (*InspectFilesArtifactContentsRequest)(nil), // 6: kurtosis_enclave_manager.InspectFilesArtifactContentsRequest - (*DownloadFilesArtifactRequest)(nil), // 7: kurtosis_enclave_manager.DownloadFilesArtifactRequest - (*GetStarlarkRunRequest)(nil), // 8: kurtosis_enclave_manager.GetStarlarkRunRequest - (*kurtosis_core_rpc_api_bindings.RunStarlarkPackageArgs)(nil), // 9: api_container_api.RunStarlarkPackageArgs - (*kurtosis_core_rpc_api_bindings.FilesArtifactNameAndUuid)(nil), // 10: api_container_api.FilesArtifactNameAndUuid - (*kurtosis_core_rpc_api_bindings.DownloadFilesArtifactArgs)(nil), // 11: api_container_api.DownloadFilesArtifactArgs - (*emptypb.Empty)(nil), // 12: google.protobuf.Empty - (*kurtosis_engine_rpc_api_bindings.GetServiceLogsArgs)(nil), // 13: engine_api.GetServiceLogsArgs - (*kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs)(nil), // 14: engine_api.CreateEnclaveArgs - (*kurtosis_engine_rpc_api_bindings.DestroyEnclaveArgs)(nil), // 15: engine_api.DestroyEnclaveArgs - (*kurtosis_engine_rpc_api_bindings.GetEnclavesResponse)(nil), // 16: engine_api.GetEnclavesResponse - (*kurtosis_core_rpc_api_bindings.GetServicesResponse)(nil), // 17: api_container_api.GetServicesResponse - (*kurtosis_engine_rpc_api_bindings.GetServiceLogsResponse)(nil), // 18: engine_api.GetServiceLogsResponse - (*kurtosis_core_rpc_api_bindings.ListFilesArtifactNamesAndUuidsResponse)(nil), // 19: api_container_api.ListFilesArtifactNamesAndUuidsResponse - (*kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine)(nil), // 20: api_container_api.StarlarkRunResponseLine - (*kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse)(nil), // 21: engine_api.CreateEnclaveResponse - (*kurtosis_core_rpc_api_bindings.InspectFilesArtifactContentsResponse)(nil), // 22: api_container_api.InspectFilesArtifactContentsResponse - (*kurtosis_core_rpc_api_bindings.StreamedDataChunk)(nil), // 23: api_container_api.StreamedDataChunk - (*kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse)(nil), // 24: api_container_api.GetStarlarkRunResponse + (*RunStarlarkScriptRequest)(nil), // 6: kurtosis_enclave_manager.RunStarlarkScriptRequest + (*InspectFilesArtifactContentsRequest)(nil), // 7: kurtosis_enclave_manager.InspectFilesArtifactContentsRequest + (*DownloadFilesArtifactRequest)(nil), // 8: kurtosis_enclave_manager.DownloadFilesArtifactRequest + (*GetStarlarkRunRequest)(nil), // 9: kurtosis_enclave_manager.GetStarlarkRunRequest + (*kurtosis_core_rpc_api_bindings.RunStarlarkPackageArgs)(nil), // 10: api_container_api.RunStarlarkPackageArgs + (*kurtosis_core_rpc_api_bindings.RunStarlarkScriptArgs)(nil), // 11: api_container_api.RunStarlarkScriptArgs + (*kurtosis_core_rpc_api_bindings.FilesArtifactNameAndUuid)(nil), // 12: api_container_api.FilesArtifactNameAndUuid + (*kurtosis_core_rpc_api_bindings.DownloadFilesArtifactArgs)(nil), // 13: api_container_api.DownloadFilesArtifactArgs + (*emptypb.Empty)(nil), // 14: google.protobuf.Empty + (*kurtosis_engine_rpc_api_bindings.GetServiceLogsArgs)(nil), // 15: engine_api.GetServiceLogsArgs + (*kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs)(nil), // 16: engine_api.CreateEnclaveArgs + (*kurtosis_engine_rpc_api_bindings.DestroyEnclaveArgs)(nil), // 17: engine_api.DestroyEnclaveArgs + (*kurtosis_engine_rpc_api_bindings.GetEnclavesResponse)(nil), // 18: engine_api.GetEnclavesResponse + (*kurtosis_core_rpc_api_bindings.GetServicesResponse)(nil), // 19: api_container_api.GetServicesResponse + (*kurtosis_engine_rpc_api_bindings.GetServiceLogsResponse)(nil), // 20: engine_api.GetServiceLogsResponse + (*kurtosis_core_rpc_api_bindings.ListFilesArtifactNamesAndUuidsResponse)(nil), // 21: api_container_api.ListFilesArtifactNamesAndUuidsResponse + (*kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine)(nil), // 22: api_container_api.StarlarkRunResponseLine + (*kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse)(nil), // 23: engine_api.CreateEnclaveResponse + (*kurtosis_core_rpc_api_bindings.InspectFilesArtifactContentsResponse)(nil), // 24: api_container_api.InspectFilesArtifactContentsResponse + (*kurtosis_core_rpc_api_bindings.StreamedDataChunk)(nil), // 25: api_container_api.StreamedDataChunk + (*kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse)(nil), // 26: api_container_api.GetStarlarkRunResponse } var file_kurtosis_enclave_manager_api_proto_depIdxs = []int32{ 0, // 0: kurtosis_enclave_manager.HealthCheckResponse.status:type_name -> kurtosis_enclave_manager.HealthCheckResponse.ServingStatus - 9, // 1: kurtosis_enclave_manager.RunStarlarkPackageRequest.RunStarlarkPackageArgs:type_name -> api_container_api.RunStarlarkPackageArgs - 10, // 2: kurtosis_enclave_manager.InspectFilesArtifactContentsRequest.file_names_and_uuid:type_name -> api_container_api.FilesArtifactNameAndUuid - 11, // 3: kurtosis_enclave_manager.DownloadFilesArtifactRequest.download_files_artifacts_args:type_name -> api_container_api.DownloadFilesArtifactArgs - 1, // 4: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.Check:input_type -> kurtosis_enclave_manager.HealthCheckRequest - 12, // 5: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetEnclaves:input_type -> google.protobuf.Empty - 3, // 6: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetServices:input_type -> kurtosis_enclave_manager.GetServicesRequest - 13, // 7: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetServiceLogs:input_type -> engine_api.GetServiceLogsArgs - 4, // 8: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.ListFilesArtifactNamesAndUuids:input_type -> kurtosis_enclave_manager.GetListFilesArtifactNamesAndUuidsRequest - 5, // 9: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkPackage:input_type -> kurtosis_enclave_manager.RunStarlarkPackageRequest - 14, // 10: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.CreateEnclave:input_type -> engine_api.CreateEnclaveArgs - 6, // 11: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.InspectFilesArtifactContents:input_type -> kurtosis_enclave_manager.InspectFilesArtifactContentsRequest - 7, // 12: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.DownloadFilesArtifact:input_type -> kurtosis_enclave_manager.DownloadFilesArtifactRequest - 15, // 13: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.DestroyEnclave:input_type -> engine_api.DestroyEnclaveArgs - 8, // 14: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetStarlarkRun:input_type -> kurtosis_enclave_manager.GetStarlarkRunRequest - 2, // 15: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.Check:output_type -> kurtosis_enclave_manager.HealthCheckResponse - 16, // 16: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetEnclaves:output_type -> engine_api.GetEnclavesResponse - 17, // 17: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetServices:output_type -> api_container_api.GetServicesResponse - 18, // 18: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetServiceLogs:output_type -> engine_api.GetServiceLogsResponse - 19, // 19: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.ListFilesArtifactNamesAndUuids:output_type -> api_container_api.ListFilesArtifactNamesAndUuidsResponse - 20, // 20: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkPackage:output_type -> api_container_api.StarlarkRunResponseLine - 21, // 21: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.CreateEnclave:output_type -> engine_api.CreateEnclaveResponse - 22, // 22: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.InspectFilesArtifactContents:output_type -> api_container_api.InspectFilesArtifactContentsResponse - 23, // 23: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.DownloadFilesArtifact:output_type -> api_container_api.StreamedDataChunk - 12, // 24: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.DestroyEnclave:output_type -> google.protobuf.Empty - 24, // 25: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetStarlarkRun:output_type -> api_container_api.GetStarlarkRunResponse - 15, // [15:26] is the sub-list for method output_type - 4, // [4:15] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 10, // 1: kurtosis_enclave_manager.RunStarlarkPackageRequest.RunStarlarkPackageArgs:type_name -> api_container_api.RunStarlarkPackageArgs + 11, // 2: kurtosis_enclave_manager.RunStarlarkScriptRequest.RunStarlarkScriptArgs:type_name -> api_container_api.RunStarlarkScriptArgs + 12, // 3: kurtosis_enclave_manager.InspectFilesArtifactContentsRequest.file_names_and_uuid:type_name -> api_container_api.FilesArtifactNameAndUuid + 13, // 4: kurtosis_enclave_manager.DownloadFilesArtifactRequest.download_files_artifacts_args:type_name -> api_container_api.DownloadFilesArtifactArgs + 1, // 5: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.Check:input_type -> kurtosis_enclave_manager.HealthCheckRequest + 14, // 6: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetEnclaves:input_type -> google.protobuf.Empty + 3, // 7: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetServices:input_type -> kurtosis_enclave_manager.GetServicesRequest + 15, // 8: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetServiceLogs:input_type -> engine_api.GetServiceLogsArgs + 4, // 9: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.ListFilesArtifactNamesAndUuids:input_type -> kurtosis_enclave_manager.GetListFilesArtifactNamesAndUuidsRequest + 5, // 10: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkPackage:input_type -> kurtosis_enclave_manager.RunStarlarkPackageRequest + 6, // 11: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkScript:input_type -> kurtosis_enclave_manager.RunStarlarkScriptRequest + 16, // 12: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.CreateEnclave:input_type -> engine_api.CreateEnclaveArgs + 7, // 13: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.InspectFilesArtifactContents:input_type -> kurtosis_enclave_manager.InspectFilesArtifactContentsRequest + 8, // 14: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.DownloadFilesArtifact:input_type -> kurtosis_enclave_manager.DownloadFilesArtifactRequest + 17, // 15: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.DestroyEnclave:input_type -> engine_api.DestroyEnclaveArgs + 9, // 16: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetStarlarkRun:input_type -> kurtosis_enclave_manager.GetStarlarkRunRequest + 2, // 17: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.Check:output_type -> kurtosis_enclave_manager.HealthCheckResponse + 18, // 18: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetEnclaves:output_type -> engine_api.GetEnclavesResponse + 19, // 19: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetServices:output_type -> api_container_api.GetServicesResponse + 20, // 20: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetServiceLogs:output_type -> engine_api.GetServiceLogsResponse + 21, // 21: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.ListFilesArtifactNamesAndUuids:output_type -> api_container_api.ListFilesArtifactNamesAndUuidsResponse + 22, // 22: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkPackage:output_type -> api_container_api.StarlarkRunResponseLine + 22, // 23: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkScript:output_type -> api_container_api.StarlarkRunResponseLine + 23, // 24: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.CreateEnclave:output_type -> engine_api.CreateEnclaveResponse + 24, // 25: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.InspectFilesArtifactContents:output_type -> api_container_api.InspectFilesArtifactContentsResponse + 25, // 26: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.DownloadFilesArtifact:output_type -> api_container_api.StreamedDataChunk + 14, // 27: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.DestroyEnclave:output_type -> google.protobuf.Empty + 26, // 28: kurtosis_enclave_manager.KurtosisEnclaveManagerServer.GetStarlarkRun:output_type -> api_container_api.GetStarlarkRunResponse + 17, // [17:29] is the sub-list for method output_type + 5, // [5:17] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_kurtosis_enclave_manager_api_proto_init() } @@ -836,7 +923,7 @@ func file_kurtosis_enclave_manager_api_proto_init() { } } file_kurtosis_enclave_manager_api_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InspectFilesArtifactContentsRequest); i { + switch v := v.(*RunStarlarkScriptRequest); i { case 0: return &v.state case 1: @@ -848,7 +935,7 @@ func file_kurtosis_enclave_manager_api_proto_init() { } } file_kurtosis_enclave_manager_api_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DownloadFilesArtifactRequest); i { + switch v := v.(*InspectFilesArtifactContentsRequest); i { case 0: return &v.state case 1: @@ -860,6 +947,18 @@ func file_kurtosis_enclave_manager_api_proto_init() { } } file_kurtosis_enclave_manager_api_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DownloadFilesArtifactRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kurtosis_enclave_manager_api_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetStarlarkRunRequest); i { case 0: return &v.state @@ -878,7 +977,7 @@ func file_kurtosis_enclave_manager_api_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_kurtosis_enclave_manager_api_proto_rawDesc, NumEnums: 1, - NumMessages: 8, + NumMessages: 9, NumExtensions: 0, NumServices: 1, }, diff --git a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_bindingsconnect/kurtosis_enclave_manager_api.connect.go b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_bindingsconnect/kurtosis_enclave_manager_api.connect.go index 46190d18f5..e51bade598 100644 --- a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_bindingsconnect/kurtosis_enclave_manager_api.connect.go +++ b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_bindingsconnect/kurtosis_enclave_manager_api.connect.go @@ -55,6 +55,9 @@ const ( // KurtosisEnclaveManagerServerRunStarlarkPackageProcedure is the fully-qualified name of the // KurtosisEnclaveManagerServer's RunStarlarkPackage RPC. KurtosisEnclaveManagerServerRunStarlarkPackageProcedure = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/RunStarlarkPackage" + // KurtosisEnclaveManagerServerRunStarlarkScriptProcedure is the fully-qualified name of the + // KurtosisEnclaveManagerServer's RunStarlarkScript RPC. + KurtosisEnclaveManagerServerRunStarlarkScriptProcedure = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/RunStarlarkScript" // KurtosisEnclaveManagerServerCreateEnclaveProcedure is the fully-qualified name of the // KurtosisEnclaveManagerServer's CreateEnclave RPC. KurtosisEnclaveManagerServerCreateEnclaveProcedure = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/CreateEnclave" @@ -81,6 +84,7 @@ type KurtosisEnclaveManagerServerClient interface { GetServiceLogs(context.Context, *connect.Request[kurtosis_engine_rpc_api_bindings.GetServiceLogsArgs]) (*connect.ServerStreamForClient[kurtosis_engine_rpc_api_bindings.GetServiceLogsResponse], error) ListFilesArtifactNamesAndUuids(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.GetListFilesArtifactNamesAndUuidsRequest]) (*connect.Response[kurtosis_core_rpc_api_bindings.ListFilesArtifactNamesAndUuidsResponse], error) RunStarlarkPackage(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.RunStarlarkPackageRequest]) (*connect.ServerStreamForClient[kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine], error) + RunStarlarkScript(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.RunStarlarkScriptRequest]) (*connect.ServerStreamForClient[kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine], error) CreateEnclave(context.Context, *connect.Request[kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs]) (*connect.Response[kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse], error) InspectFilesArtifactContents(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.InspectFilesArtifactContentsRequest]) (*connect.Response[kurtosis_core_rpc_api_bindings.InspectFilesArtifactContentsResponse], error) DownloadFilesArtifact(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.DownloadFilesArtifactRequest]) (*connect.ServerStreamForClient[kurtosis_core_rpc_api_bindings.StreamedDataChunk], error) @@ -129,6 +133,11 @@ func NewKurtosisEnclaveManagerServerClient(httpClient connect.HTTPClient, baseUR baseURL+KurtosisEnclaveManagerServerRunStarlarkPackageProcedure, opts..., ), + runStarlarkScript: connect.NewClient[kurtosis_enclave_manager_api_bindings.RunStarlarkScriptRequest, kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine]( + httpClient, + baseURL+KurtosisEnclaveManagerServerRunStarlarkScriptProcedure, + opts..., + ), createEnclave: connect.NewClient[kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs, kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse]( httpClient, baseURL+KurtosisEnclaveManagerServerCreateEnclaveProcedure, @@ -165,6 +174,7 @@ type kurtosisEnclaveManagerServerClient struct { getServiceLogs *connect.Client[kurtosis_engine_rpc_api_bindings.GetServiceLogsArgs, kurtosis_engine_rpc_api_bindings.GetServiceLogsResponse] listFilesArtifactNamesAndUuids *connect.Client[kurtosis_enclave_manager_api_bindings.GetListFilesArtifactNamesAndUuidsRequest, kurtosis_core_rpc_api_bindings.ListFilesArtifactNamesAndUuidsResponse] runStarlarkPackage *connect.Client[kurtosis_enclave_manager_api_bindings.RunStarlarkPackageRequest, kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine] + runStarlarkScript *connect.Client[kurtosis_enclave_manager_api_bindings.RunStarlarkScriptRequest, kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine] createEnclave *connect.Client[kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs, kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse] inspectFilesArtifactContents *connect.Client[kurtosis_enclave_manager_api_bindings.InspectFilesArtifactContentsRequest, kurtosis_core_rpc_api_bindings.InspectFilesArtifactContentsResponse] downloadFilesArtifact *connect.Client[kurtosis_enclave_manager_api_bindings.DownloadFilesArtifactRequest, kurtosis_core_rpc_api_bindings.StreamedDataChunk] @@ -204,6 +214,11 @@ func (c *kurtosisEnclaveManagerServerClient) RunStarlarkPackage(ctx context.Cont return c.runStarlarkPackage.CallServerStream(ctx, req) } +// RunStarlarkScript calls kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkScript. +func (c *kurtosisEnclaveManagerServerClient) RunStarlarkScript(ctx context.Context, req *connect.Request[kurtosis_enclave_manager_api_bindings.RunStarlarkScriptRequest]) (*connect.ServerStreamForClient[kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine], error) { + return c.runStarlarkScript.CallServerStream(ctx, req) +} + // CreateEnclave calls kurtosis_enclave_manager.KurtosisEnclaveManagerServer.CreateEnclave. func (c *kurtosisEnclaveManagerServerClient) CreateEnclave(ctx context.Context, req *connect.Request[kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs]) (*connect.Response[kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse], error) { return c.createEnclave.CallUnary(ctx, req) @@ -240,6 +255,7 @@ type KurtosisEnclaveManagerServerHandler interface { GetServiceLogs(context.Context, *connect.Request[kurtosis_engine_rpc_api_bindings.GetServiceLogsArgs], *connect.ServerStream[kurtosis_engine_rpc_api_bindings.GetServiceLogsResponse]) error ListFilesArtifactNamesAndUuids(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.GetListFilesArtifactNamesAndUuidsRequest]) (*connect.Response[kurtosis_core_rpc_api_bindings.ListFilesArtifactNamesAndUuidsResponse], error) RunStarlarkPackage(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.RunStarlarkPackageRequest], *connect.ServerStream[kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine]) error + RunStarlarkScript(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.RunStarlarkScriptRequest], *connect.ServerStream[kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine]) error CreateEnclave(context.Context, *connect.Request[kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs]) (*connect.Response[kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse], error) InspectFilesArtifactContents(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.InspectFilesArtifactContentsRequest]) (*connect.Response[kurtosis_core_rpc_api_bindings.InspectFilesArtifactContentsResponse], error) DownloadFilesArtifact(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.DownloadFilesArtifactRequest], *connect.ServerStream[kurtosis_core_rpc_api_bindings.StreamedDataChunk]) error @@ -283,6 +299,11 @@ func NewKurtosisEnclaveManagerServerHandler(svc KurtosisEnclaveManagerServerHand svc.RunStarlarkPackage, opts..., ) + kurtosisEnclaveManagerServerRunStarlarkScriptHandler := connect.NewServerStreamHandler( + KurtosisEnclaveManagerServerRunStarlarkScriptProcedure, + svc.RunStarlarkScript, + opts..., + ) kurtosisEnclaveManagerServerCreateEnclaveHandler := connect.NewUnaryHandler( KurtosisEnclaveManagerServerCreateEnclaveProcedure, svc.CreateEnclave, @@ -322,6 +343,8 @@ func NewKurtosisEnclaveManagerServerHandler(svc KurtosisEnclaveManagerServerHand kurtosisEnclaveManagerServerListFilesArtifactNamesAndUuidsHandler.ServeHTTP(w, r) case KurtosisEnclaveManagerServerRunStarlarkPackageProcedure: kurtosisEnclaveManagerServerRunStarlarkPackageHandler.ServeHTTP(w, r) + case KurtosisEnclaveManagerServerRunStarlarkScriptProcedure: + kurtosisEnclaveManagerServerRunStarlarkScriptHandler.ServeHTTP(w, r) case KurtosisEnclaveManagerServerCreateEnclaveProcedure: kurtosisEnclaveManagerServerCreateEnclaveHandler.ServeHTTP(w, r) case KurtosisEnclaveManagerServerInspectFilesArtifactContentsProcedure: @@ -365,6 +388,10 @@ func (UnimplementedKurtosisEnclaveManagerServerHandler) RunStarlarkPackage(conte return connect.NewError(connect.CodeUnimplemented, errors.New("kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkPackage is not implemented")) } +func (UnimplementedKurtosisEnclaveManagerServerHandler) RunStarlarkScript(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.RunStarlarkScriptRequest], *connect.ServerStream[kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkScript is not implemented")) +} + func (UnimplementedKurtosisEnclaveManagerServerHandler) CreateEnclave(context.Context, *connect.Request[kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs]) (*connect.Response[kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("kurtosis_enclave_manager.KurtosisEnclaveManagerServer.CreateEnclave is not implemented")) } diff --git a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_grpc.pb.go b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_grpc.pb.go index cf3a660e8a..8495d11622 100644 --- a/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_grpc.pb.go +++ b/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_grpc.pb.go @@ -28,6 +28,7 @@ const ( KurtosisEnclaveManagerServer_GetServiceLogs_FullMethodName = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/GetServiceLogs" KurtosisEnclaveManagerServer_ListFilesArtifactNamesAndUuids_FullMethodName = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/ListFilesArtifactNamesAndUuids" KurtosisEnclaveManagerServer_RunStarlarkPackage_FullMethodName = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/RunStarlarkPackage" + KurtosisEnclaveManagerServer_RunStarlarkScript_FullMethodName = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/RunStarlarkScript" KurtosisEnclaveManagerServer_CreateEnclave_FullMethodName = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/CreateEnclave" KurtosisEnclaveManagerServer_InspectFilesArtifactContents_FullMethodName = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/InspectFilesArtifactContents" KurtosisEnclaveManagerServer_DownloadFilesArtifact_FullMethodName = "/kurtosis_enclave_manager.KurtosisEnclaveManagerServer/DownloadFilesArtifact" @@ -45,6 +46,7 @@ type KurtosisEnclaveManagerServerClient interface { GetServiceLogs(ctx context.Context, in *kurtosis_engine_rpc_api_bindings.GetServiceLogsArgs, opts ...grpc.CallOption) (KurtosisEnclaveManagerServer_GetServiceLogsClient, error) ListFilesArtifactNamesAndUuids(ctx context.Context, in *GetListFilesArtifactNamesAndUuidsRequest, opts ...grpc.CallOption) (*kurtosis_core_rpc_api_bindings.ListFilesArtifactNamesAndUuidsResponse, error) RunStarlarkPackage(ctx context.Context, in *RunStarlarkPackageRequest, opts ...grpc.CallOption) (KurtosisEnclaveManagerServer_RunStarlarkPackageClient, error) + RunStarlarkScript(ctx context.Context, in *RunStarlarkScriptRequest, opts ...grpc.CallOption) (KurtosisEnclaveManagerServer_RunStarlarkScriptClient, error) CreateEnclave(ctx context.Context, in *kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs, opts ...grpc.CallOption) (*kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse, error) InspectFilesArtifactContents(ctx context.Context, in *InspectFilesArtifactContentsRequest, opts ...grpc.CallOption) (*kurtosis_core_rpc_api_bindings.InspectFilesArtifactContentsResponse, error) DownloadFilesArtifact(ctx context.Context, in *DownloadFilesArtifactRequest, opts ...grpc.CallOption) (KurtosisEnclaveManagerServer_DownloadFilesArtifactClient, error) @@ -160,6 +162,38 @@ func (x *kurtosisEnclaveManagerServerRunStarlarkPackageClient) Recv() (*kurtosis return m, nil } +func (c *kurtosisEnclaveManagerServerClient) RunStarlarkScript(ctx context.Context, in *RunStarlarkScriptRequest, opts ...grpc.CallOption) (KurtosisEnclaveManagerServer_RunStarlarkScriptClient, error) { + stream, err := c.cc.NewStream(ctx, &KurtosisEnclaveManagerServer_ServiceDesc.Streams[2], KurtosisEnclaveManagerServer_RunStarlarkScript_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &kurtosisEnclaveManagerServerRunStarlarkScriptClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type KurtosisEnclaveManagerServer_RunStarlarkScriptClient interface { + Recv() (*kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine, error) + grpc.ClientStream +} + +type kurtosisEnclaveManagerServerRunStarlarkScriptClient struct { + grpc.ClientStream +} + +func (x *kurtosisEnclaveManagerServerRunStarlarkScriptClient) Recv() (*kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine, error) { + m := new(kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func (c *kurtosisEnclaveManagerServerClient) CreateEnclave(ctx context.Context, in *kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs, opts ...grpc.CallOption) (*kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse, error) { out := new(kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse) err := c.cc.Invoke(ctx, KurtosisEnclaveManagerServer_CreateEnclave_FullMethodName, in, out, opts...) @@ -179,7 +213,7 @@ func (c *kurtosisEnclaveManagerServerClient) InspectFilesArtifactContents(ctx co } func (c *kurtosisEnclaveManagerServerClient) DownloadFilesArtifact(ctx context.Context, in *DownloadFilesArtifactRequest, opts ...grpc.CallOption) (KurtosisEnclaveManagerServer_DownloadFilesArtifactClient, error) { - stream, err := c.cc.NewStream(ctx, &KurtosisEnclaveManagerServer_ServiceDesc.Streams[2], KurtosisEnclaveManagerServer_DownloadFilesArtifact_FullMethodName, opts...) + stream, err := c.cc.NewStream(ctx, &KurtosisEnclaveManagerServer_ServiceDesc.Streams[3], KurtosisEnclaveManagerServer_DownloadFilesArtifact_FullMethodName, opts...) if err != nil { return nil, err } @@ -238,6 +272,7 @@ type KurtosisEnclaveManagerServerServer interface { GetServiceLogs(*kurtosis_engine_rpc_api_bindings.GetServiceLogsArgs, KurtosisEnclaveManagerServer_GetServiceLogsServer) error ListFilesArtifactNamesAndUuids(context.Context, *GetListFilesArtifactNamesAndUuidsRequest) (*kurtosis_core_rpc_api_bindings.ListFilesArtifactNamesAndUuidsResponse, error) RunStarlarkPackage(*RunStarlarkPackageRequest, KurtosisEnclaveManagerServer_RunStarlarkPackageServer) error + RunStarlarkScript(*RunStarlarkScriptRequest, KurtosisEnclaveManagerServer_RunStarlarkScriptServer) error CreateEnclave(context.Context, *kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs) (*kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse, error) InspectFilesArtifactContents(context.Context, *InspectFilesArtifactContentsRequest) (*kurtosis_core_rpc_api_bindings.InspectFilesArtifactContentsResponse, error) DownloadFilesArtifact(*DownloadFilesArtifactRequest, KurtosisEnclaveManagerServer_DownloadFilesArtifactServer) error @@ -267,6 +302,9 @@ func (UnimplementedKurtosisEnclaveManagerServerServer) ListFilesArtifactNamesAnd func (UnimplementedKurtosisEnclaveManagerServerServer) RunStarlarkPackage(*RunStarlarkPackageRequest, KurtosisEnclaveManagerServer_RunStarlarkPackageServer) error { return status.Errorf(codes.Unimplemented, "method RunStarlarkPackage not implemented") } +func (UnimplementedKurtosisEnclaveManagerServerServer) RunStarlarkScript(*RunStarlarkScriptRequest, KurtosisEnclaveManagerServer_RunStarlarkScriptServer) error { + return status.Errorf(codes.Unimplemented, "method RunStarlarkScript not implemented") +} func (UnimplementedKurtosisEnclaveManagerServerServer) CreateEnclave(context.Context, *kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs) (*kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateEnclave not implemented") } @@ -408,6 +446,27 @@ func (x *kurtosisEnclaveManagerServerRunStarlarkPackageServer) Send(m *kurtosis_ return x.ServerStream.SendMsg(m) } +func _KurtosisEnclaveManagerServer_RunStarlarkScript_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(RunStarlarkScriptRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(KurtosisEnclaveManagerServerServer).RunStarlarkScript(m, &kurtosisEnclaveManagerServerRunStarlarkScriptServer{stream}) +} + +type KurtosisEnclaveManagerServer_RunStarlarkScriptServer interface { + Send(*kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine) error + grpc.ServerStream +} + +type kurtosisEnclaveManagerServerRunStarlarkScriptServer struct { + grpc.ServerStream +} + +func (x *kurtosisEnclaveManagerServerRunStarlarkScriptServer) Send(m *kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine) error { + return x.ServerStream.SendMsg(m) +} + func _KurtosisEnclaveManagerServer_CreateEnclave_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs) if err := dec(in); err != nil { @@ -552,6 +611,11 @@ var KurtosisEnclaveManagerServer_ServiceDesc = grpc.ServiceDesc{ Handler: _KurtosisEnclaveManagerServer_RunStarlarkPackage_Handler, ServerStreams: true, }, + { + StreamName: "RunStarlarkScript", + Handler: _KurtosisEnclaveManagerServer_RunStarlarkScript_Handler, + ServerStreams: true, + }, { StreamName: "DownloadFilesArtifact", Handler: _KurtosisEnclaveManagerServer_DownloadFilesArtifact_Handler, diff --git a/enclave-manager/api/protobuf/kurtosis_enclave_manager_api.proto b/enclave-manager/api/protobuf/kurtosis_enclave_manager_api.proto index 2550209763..76a9512bf4 100644 --- a/enclave-manager/api/protobuf/kurtosis_enclave_manager_api.proto +++ b/enclave-manager/api/protobuf/kurtosis_enclave_manager_api.proto @@ -15,6 +15,7 @@ service KurtosisEnclaveManagerServer { rpc GetServiceLogs(engine_api.GetServiceLogsArgs) returns (stream engine_api.GetServiceLogsResponse) {}; rpc ListFilesArtifactNamesAndUuids(GetListFilesArtifactNamesAndUuidsRequest) returns (api_container_api.ListFilesArtifactNamesAndUuidsResponse) {}; rpc RunStarlarkPackage(RunStarlarkPackageRequest) returns (stream api_container_api.StarlarkRunResponseLine) {}; + rpc RunStarlarkScript(RunStarlarkScriptRequest) returns (stream api_container_api.StarlarkRunResponseLine) {}; rpc CreateEnclave(engine_api.CreateEnclaveArgs) returns (engine_api.CreateEnclaveResponse) {}; rpc InspectFilesArtifactContents(InspectFilesArtifactContentsRequest) returns (api_container_api.InspectFilesArtifactContentsResponse) {}; rpc DownloadFilesArtifact(DownloadFilesArtifactRequest) returns (stream api_container_api.StreamedDataChunk) {}; @@ -52,6 +53,12 @@ message RunStarlarkPackageRequest{ api_container_api.RunStarlarkPackageArgs RunStarlarkPackageArgs = 3; } +message RunStarlarkScriptRequest{ + string apic_ip_address = 1; + int32 apic_port = 2; + api_container_api.RunStarlarkScriptArgs RunStarlarkScriptArgs = 3; +} + message InspectFilesArtifactContentsRequest { string apic_ip_address = 1; int32 apic_port = 2; diff --git a/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_connect.ts b/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_connect.ts index 5a2d85d6a5..9f53d33c27 100644 --- a/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_connect.ts +++ b/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_connect.ts @@ -3,7 +3,7 @@ /* eslint-disable */ // @ts-nocheck -import { DownloadFilesArtifactRequest, GetListFilesArtifactNamesAndUuidsRequest, GetServicesRequest, GetStarlarkRunRequest, HealthCheckRequest, HealthCheckResponse, InspectFilesArtifactContentsRequest, RunStarlarkPackageRequest } from "./kurtosis_enclave_manager_api_pb.js"; +import { DownloadFilesArtifactRequest, GetListFilesArtifactNamesAndUuidsRequest, GetServicesRequest, GetStarlarkRunRequest, HealthCheckRequest, HealthCheckResponse, InspectFilesArtifactContentsRequest, RunStarlarkPackageRequest, RunStarlarkScriptRequest } from "./kurtosis_enclave_manager_api_pb.js"; import { Empty, MethodKind } from "@bufbuild/protobuf"; import { CreateEnclaveArgs, CreateEnclaveResponse, DestroyEnclaveArgs, GetEnclavesResponse, GetServiceLogsArgs, GetServiceLogsResponse } from "./engine_service_pb.js"; import { GetServicesResponse, GetStarlarkRunResponse, InspectFilesArtifactContentsResponse, ListFilesArtifactNamesAndUuidsResponse, StarlarkRunResponseLine, StreamedDataChunk } from "./api_container_service_pb.js"; @@ -68,6 +68,15 @@ export const KurtosisEnclaveManagerServer = { O: StarlarkRunResponseLine, kind: MethodKind.ServerStreaming, }, + /** + * @generated from rpc kurtosis_enclave_manager.KurtosisEnclaveManagerServer.RunStarlarkScript + */ + runStarlarkScript: { + name: "RunStarlarkScript", + I: RunStarlarkScriptRequest, + O: StarlarkRunResponseLine, + kind: MethodKind.ServerStreaming, + }, /** * @generated from rpc kurtosis_enclave_manager.KurtosisEnclaveManagerServer.CreateEnclave */ diff --git a/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_pb.ts b/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_pb.ts index 4e975eb423..48ade988d6 100644 --- a/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_pb.ts +++ b/enclave-manager/api/typescript/src/kurtosis_enclave_manager_api_pb.ts @@ -5,7 +5,7 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; -import { DownloadFilesArtifactArgs, FilesArtifactNameAndUuid, RunStarlarkPackageArgs } from "./api_container_service_pb.js"; +import { DownloadFilesArtifactArgs, FilesArtifactNameAndUuid, RunStarlarkPackageArgs, RunStarlarkScriptArgs } from "./api_container_service_pb.js"; /** * @generated from message kurtosis_enclave_manager.HealthCheckRequest @@ -250,6 +250,55 @@ export class RunStarlarkPackageRequest extends Message { + /** + * @generated from field: string apic_ip_address = 1; + */ + apicIpAddress = ""; + + /** + * @generated from field: int32 apic_port = 2; + */ + apicPort = 0; + + /** + * @generated from field: api_container_api.RunStarlarkScriptArgs RunStarlarkScriptArgs = 3; + */ + RunStarlarkScriptArgs?: RunStarlarkScriptArgs; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "kurtosis_enclave_manager.RunStarlarkScriptRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "apic_ip_address", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "apic_port", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, + { no: 3, name: "RunStarlarkScriptArgs", kind: "message", T: RunStarlarkScriptArgs }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): RunStarlarkScriptRequest { + return new RunStarlarkScriptRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): RunStarlarkScriptRequest { + return new RunStarlarkScriptRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): RunStarlarkScriptRequest { + return new RunStarlarkScriptRequest().fromJsonString(jsonString, options); + } + + static equals(a: RunStarlarkScriptRequest | PlainMessage | undefined, b: RunStarlarkScriptRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(RunStarlarkScriptRequest, a, b); + } +} + /** * @generated from message kurtosis_enclave_manager.InspectFilesArtifactContentsRequest */ diff --git a/enclave-manager/server/server.go b/enclave-manager/server/server.go index 914a88d468..3b4dabb8ff 100644 --- a/enclave-manager/server/server.go +++ b/enclave-manager/server/server.go @@ -217,11 +217,8 @@ func (c *WebServer) ListFilesArtifactNamesAndUuids(ctx context.Context, req *con return resp, nil } -func (c *WebServer) RunStarlarkPackage(ctx context.Context, req *connect.Request[kurtosis_enclave_manager_api_bindings.RunStarlarkPackageRequest], str *connect.ServerStream[kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine]) error { +func (c *WebServer) RunStarlarkPackage(ctx context.Context, req *connect.Request[kurtosis_enclave_manager_api_bindings.RunStarlarkPackageRequest], responseStream *connect.ServerStream[kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine]) error { apiContainerServiceClient, err := c.createAPICClient(req.Msg.ApicIpAddress, req.Msg.ApicPort) - runPackageArgs := req.Msg.RunStarlarkPackageArgs - shouldClonePackage := true - runPackageArgs.ClonePackage = &shouldClonePackage if err != nil { return stacktrace.Propagate(err, "Failed to create the APIC client") } @@ -229,6 +226,10 @@ func (c *WebServer) RunStarlarkPackage(ctx context.Context, req *connect.Request Msg: req.Msg.RunStarlarkPackageArgs, } + runPackageArgs := req.Msg.RunStarlarkPackageArgs + shouldClonePackage := true // ktoday: Why do we coerce the "clone" to true? + runPackageArgs.ClonePackage = &shouldClonePackage + starlarkLogsStream, err := (*apiContainerServiceClient).RunStarlarkPackage(ctx, runStarlarkRequest) if err != nil { return stacktrace.Propagate(err, "Failed to run package: %s", req.Msg.RunStarlarkPackageArgs.PackageId) @@ -236,13 +237,43 @@ func (c *WebServer) RunStarlarkPackage(ctx context.Context, req *connect.Request for starlarkLogsStream.Receive() { resp := starlarkLogsStream.Msg() - err = str.Send(resp) + err = responseStream.Send(resp) + if err != nil { + return stacktrace.Propagate(err, "An error occurred in the enclave manager server attempting to return logs from running the Starlark package.") + } + } + if err = starlarkLogsStream.Err(); err != nil { + return stacktrace.Propagate(err, "An error occurred in the enclave manager server attempting to return logs from running the Starlark package.") + } + + return nil +} + +func (c *WebServer) RunStarlarkScript(ctx context.Context, req *connect.Request[kurtosis_enclave_manager_api_bindings.RunStarlarkScriptRequest], responseStream *connect.ServerStream[kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine]) error { + apiContainerServiceClient, err := c.createAPICClient(req.Msg.ApicIpAddress, req.Msg.ApicPort) + if err != nil { + return stacktrace.Propagate(err, "Failed to create the APIC client") + } + + runScriptArgs := req.Msg.RunStarlarkScriptArgs + runStarlarkRequest := &connect.Request[kurtosis_core_rpc_api_bindings.RunStarlarkScriptArgs]{ + Msg: req.Msg.RunStarlarkScriptArgs, + } + + starlarkLogsStream, err := (*apiContainerServiceClient).RunStarlarkScript(ctx, runStarlarkRequest) + if err != nil { + return stacktrace.Propagate(err, "Failed to run the following Starlark script:\n%s", runScriptArgs.SerializedScript) + } + + for starlarkLogsStream.Receive() { + resp := starlarkLogsStream.Msg() + err = responseStream.Send(resp) if err != nil { - return stacktrace.Propagate(err, "An error occurred in the enclave manager server attempting to send run starlark package logs.") + return stacktrace.Propagate(err, "An error occurred in the enclave manager server attempting to return logs from running the Starlark script.") } } if err = starlarkLogsStream.Err(); err != nil { - return stacktrace.Propagate(err, "An error occurred in the enclave manager server attempting to receive run starlark package logs.") + return stacktrace.Propagate(err, "An error occurred in the enclave manager server attempting to return logs from running the Starlark script.") } return nil From 0ce07113cf2501443691c328ca3ad09b8d5b1b84 Mon Sep 17 00:00:00 2001 From: leoporoli Date: Thu, 1 Feb 2024 12:33:02 -0300 Subject: [PATCH 020/102] chore: debug engine configuration and readme docs (#2101) ## Description: debug engine configuration and readme docs ## Is this change user facing? NO ## References (if applicable): --- .run/Engine-remote-debug.run.xml | 6 + README.md | 43 ++++ cli/cli/commands/enclave/add/add.go | 1 + cli/cli/commands/engine/restart/restart.go | 14 +- cli/cli/commands/engine/start/start.go | 14 +- cli/cli/commands/root.go | 6 + cli/cli/defaults/defaults.go | 5 + .../engine_existence_guarantor.go | 9 + .../helpers/engine_manager/engine_manager.go | 10 +- cli/cli/scripts/debug-cli.sh | 19 +- .../docker_kurtosis_backend.go | 2 + .../engine_functions/create_engine.go | 47 +++- .../docker_manager/container_capabilities.go | 3 +- .../docker_manager/container_security_opt.go | 7 + .../create_and_start_container_args.go | 11 + .../docker/docker_manager/docker_manager.go | 10 +- .../engine_functions/create_engine.go | 1 + .../kubernetes_kurtosis_backend.go | 2 + .../metrics_reporting_kurtosis_backend.go | 4 +- .../lib/backend_interface/kurtosis_backend.go | 1 + .../mock_kurtosis_backend.go | 204 ++++++++++++++++-- .../engine_server_launcher.go | 7 +- engine/scripts/build.sh | 23 +- engine/server/.dockerignore | 1 + engine/server/Dockerfile.debug | 21 ++ engine/server/scripts/_constants.env | 10 + engine/server/scripts/build.sh | 86 +++++++- .../goland-engine-breakpoint.png | Bin 0 -> 55923 bytes .../goland-engine-debug-button.png | Bin 0 -> 12669 bytes scripts/build.sh | 51 ++++- scripts/port-forward-engine-debug.sh | 22 ++ scripts/set_kt_alias.sh | 0 32 files changed, 588 insertions(+), 52 deletions(-) create mode 100644 .run/Engine-remote-debug.run.xml create mode 100644 container-engine-lib/lib/backend_impls/docker/docker_manager/container_security_opt.go create mode 100644 engine/server/Dockerfile.debug create mode 100644 readme-static-files/goland-engine-breakpoint.png create mode 100644 readme-static-files/goland-engine-debug-button.png create mode 100755 scripts/port-forward-engine-debug.sh mode change 100644 => 100755 scripts/set_kt_alias.sh diff --git a/.run/Engine-remote-debug.run.xml b/.run/Engine-remote-debug.run.xml new file mode 100644 index 0000000000..2ba9ce0aa7 --- /dev/null +++ b/.run/Engine-remote-debug.run.xml @@ -0,0 +1,6 @@ + + + + diff --git a/README.md b/README.md index 2505b7af91..97264e8b52 100644 --- a/README.md +++ b/README.md @@ -393,6 +393,7 @@ ktdebug version For running CLI with Delve debug client: + 1. Build the CLI dev binary and run the command you want to debug (kurtosis version in this example), but first pass "dlv-terminal" as the first argument (this will start the Delve client in the terminal) ```bash cli/cli/scripts/build.sh @@ -410,6 +411,48 @@ ktdebug dlv-terminal version 4. You can see [more Delve commands here][delve-docs] + +For running Kurtosis engine with Golang remote debug: + +1. Run the main build script with the first argument `debug_mode` as true. This will generate a new Kurtosis engine container image which will contain the `debug` suffix in the name. +```bash +scripts/build.sh true +``` +2. Add the breakpoint in the line where you want to stop the cursor + +3. Run the engine in debug mode with the `ktdev engine start --debug-mode` or the `ktdev engine restart --debug-mode` commands +```bash +source ./scripts/set_kt_alias.sh +ktdev engine start --debug-mode +``` +4. Then choose the "Engine-remote-debug" run configuration in the "run panel" +5. Press the "debug" button + +6. Use the debug panel to inspect the variables value and continue with the debug flow + +7. Make a call to the engine's server (you can use the Kurtosis CLI or Postman) in order to reach out the breakpoint in the code +8. You can debug the CLI and the Kurtosis engine's server at the same time by running it with `ktdebug` instead of `ktdev` mentioned in a previous step, remember to run both remote debug configuration in the Goland IDE. +```bash +source ./scripts/set_kt_alias.sh +ktdebug engine start +``` + +Additional steps if you are debugging Kurtosis engine in K8s: + +1. Upload the engine's image for debug to the K8s cluster +```bash +# for example: +k3d image load kurtosistech/engine:5ec6eb-dirty-debug +``` +2. Run the port-forward script before pressing the debug button in Golang (in another terminal instance) to bind the host's port to the container's debug server port +```bash +scripts/port-forward-engine-debug.sh +``` +3. Do not forget to run the Kurtosis gateway after calling the engine's server (in another terminal instance also) +```bash +ktdev gateway +``` + diff --git a/cli/cli/commands/enclave/add/add.go b/cli/cli/commands/enclave/add/add.go index 6f4ec9a401..7e2e8293ff 100644 --- a/cli/cli/commands/enclave/add/add.go +++ b/cli/cli/commands/enclave/add/add.go @@ -107,6 +107,7 @@ 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) if err != nil { return stacktrace.Propagate(err, "An error occurred creating a new Kurtosis engine client") diff --git a/cli/cli/commands/engine/restart/restart.go b/cli/cli/commands/engine/restart/restart.go index 6038f9a756..a45ae86dab 100644 --- a/cli/cli/commands/engine/restart/restart.go +++ b/cli/cli/commands/engine/restart/restart.go @@ -11,6 +11,7 @@ import ( "github.com/kurtosis-tech/kurtosis/cli/cli/defaults" "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/engine_manager" "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/logrus_log_levels" + "github.com/kurtosis-tech/kurtosis/kurtosis_version" "github.com/kurtosis-tech/stacktrace" "github.com/sirupsen/logrus" "strconv" @@ -102,9 +103,20 @@ func run(_ context.Context, flags *flags.ParsedFlags, _ *args.ParsedArgs) error return stacktrace.Propagate(err, "An error occurred while getting the Kurtosis engine Container Version using flag with key '%v'; this is a bug in Kurtosis", engineVersionFlagKey) } + isDebugMode, err := flags.GetBool(defaults.DebugModeFlagKey) + if err != nil { + return stacktrace.Propagate(err, "Expected a value for the '%v' flag but failed to get it", defaults.DebugModeFlagKey) + } + + shouldStartInDebugMode := defaults.DefaultEnableDebugMode + if isDebugMode { + engineVersion = fmt.Sprintf("%s-%s", kurtosis_version.KurtosisVersion, defaults.DefaultKurtosisContainerDebugImageNameSuffix) + shouldStartInDebugMode = true + } + var engineClientCloseFunc func() error var restartEngineErr error - _, engineClientCloseFunc, restartEngineErr = engineManager.RestartEngineIdempotently(ctx, logLevel, engineVersion, restartEngineOnSameVersionIfAnyRunning, enclavePoolSize) + _, engineClientCloseFunc, restartEngineErr = engineManager.RestartEngineIdempotently(ctx, logLevel, engineVersion, restartEngineOnSameVersionIfAnyRunning, enclavePoolSize, shouldStartInDebugMode) if restartEngineErr != nil { return stacktrace.Propagate(restartEngineErr, "An error occurred restarting the Kurtosis engine") } diff --git a/cli/cli/commands/engine/start/start.go b/cli/cli/commands/engine/start/start.go index 585ab4edb4..86f93089e2 100644 --- a/cli/cli/commands/engine/start/start.go +++ b/cli/cli/commands/engine/start/start.go @@ -105,12 +105,22 @@ func run(_ context.Context, flags *flags.ParsedFlags, _ *args.ParsedArgs) error return stacktrace.Propagate(err, "An error occurred while getting the Kurtosis engine Container Version using flag with key '%v'; this is a bug in Kurtosis", engineVersionFlagKey) } - if engineVersion == defaultEngineVersion { + isDebugMode, err := flags.GetBool(defaults.DebugModeFlagKey) + if err != nil { + return stacktrace.Propagate(err, "Expected a value for the '%v' flag but failed to get it", defaults.DebugModeFlagKey) + } + + 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) + } 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) } else { logrus.Infof("Starting Kurtosis engine from image '%v%v%v'...", kurtosisTechEngineImagePrefix, imageVersionDelimiter, engineVersion) - _, engineClientCloseFunc, startEngineErr = engineManager.StartEngineIdempotentlyWithCustomVersion(ctx, engineVersion, logLevel, enclavePoolSize) + _, engineClientCloseFunc, startEngineErr = engineManager.StartEngineIdempotentlyWithCustomVersion(ctx, engineVersion, logLevel, enclavePoolSize, defaults.DefaultEnableDebugMode) } if startEngineErr != nil { return stacktrace.Propagate(startEngineErr, "An error occurred starting the Kurtosis engine") diff --git a/cli/cli/commands/root.go b/cli/cli/commands/root.go index aee6232d9e..2aa5c563a9 100644 --- a/cli/cli/commands/root.go +++ b/cli/cli/commands/root.go @@ -35,6 +35,7 @@ import ( "github.com/kurtosis-tech/kurtosis/cli/cli/commands/twitter" "github.com/kurtosis-tech/kurtosis/cli/cli/commands/version" "github.com/kurtosis-tech/kurtosis/cli/cli/commands/web" + "github.com/kurtosis-tech/kurtosis/cli/cli/defaults" "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/host_machine_directories" "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/logrus_log_levels" "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/user_send_metrics_election" @@ -106,6 +107,11 @@ func init() { defaultLogLevelStr, "Sets the level that the CLI will log at ("+strings.Join(logrus_log_levels.GetAcceptableLogLevelStrs(), "|")+")", ) + RootCmd.PersistentFlags().Bool( + defaults.DebugModeFlagKey, + defaults.DefaultEnableDebugMode, + "Whether should enable Kurtosis in debug mode. The debug mode will use the Kurtosis container debug images version (only enabled for the engine server so far)", + ) RootCmd.AddCommand(analytics.AnalyticsCmd.MustGetCobraCommand()) RootCmd.AddCommand(clean.CleanCmd.MustGetCobraCommand()) diff --git a/cli/cli/defaults/defaults.go b/cli/cli/defaults/defaults.go index a7e4fdeb09..3f32456b1b 100644 --- a/cli/cli/defaults/defaults.go +++ b/cli/cli/defaults/defaults.go @@ -17,6 +17,11 @@ const ( // engine-enclave-pool-size = 0 means that enclave pool feat will be disabled DefaultEngineEnclavePoolSize uint8 = 0 + + // This is the persistent flag key used, accroos all the CLI commands, to determine wheter to run in debug mode + DebugModeFlagKey = "debug-mode" + DefaultEnableDebugMode = false + DefaultKurtosisContainerDebugImageNameSuffix = "debug" ) var DefaultApiContainerLogLevel = logrus.DebugLevel diff --git a/cli/cli/helpers/engine_manager/engine_existence_guarantor.go b/cli/cli/helpers/engine_manager/engine_existence_guarantor.go index f109930cba..a21f5992d4 100644 --- a/cli/cli/helpers/engine_manager/engine_existence_guarantor.go +++ b/cli/cli/helpers/engine_manager/engine_existence_guarantor.go @@ -74,6 +74,9 @@ type engineExistenceGuarantor struct { enclaveEnvVars string allowedCORSOrigins *[]string + + // Whether the engine's should run with the debug server to receive a remote debug connection + shouldRunInDebugMode bool } func newEngineExistenceGuarantorWithDefaultVersion( @@ -89,6 +92,7 @@ func newEngineExistenceGuarantorWithDefaultVersion( poolSize uint8, enclaveEnvVars string, allowedCORSOrigins *[]string, + shouldRunInDebugMode bool, ) *engineExistenceGuarantor { return newEngineExistenceGuarantorWithCustomVersion( ctx, @@ -104,6 +108,7 @@ func newEngineExistenceGuarantorWithDefaultVersion( poolSize, enclaveEnvVars, allowedCORSOrigins, + shouldRunInDebugMode, ) } @@ -121,6 +126,7 @@ func newEngineExistenceGuarantorWithCustomVersion( poolSize uint8, enclaveEnvVars string, allowedCORSOrigins *[]string, + shouldRunInDebugMode bool, ) *engineExistenceGuarantor { return &engineExistenceGuarantor{ ctx: ctx, @@ -138,6 +144,7 @@ func newEngineExistenceGuarantorWithCustomVersion( poolSize: poolSize, enclaveEnvVars: enclaveEnvVars, allowedCORSOrigins: allowedCORSOrigins, + shouldRunInDebugMode: shouldRunInDebugMode, } } @@ -173,6 +180,7 @@ func (guarantor *engineExistenceGuarantor) VisitStopped() error { maybeCloudUserId, maybeCloudInstanceId, guarantor.allowedCORSOrigins, + guarantor.shouldRunInDebugMode, ) } else { _, _, engineLaunchErr = guarantor.engineServerLauncher.LaunchWithCustomVersion( @@ -190,6 +198,7 @@ func (guarantor *engineExistenceGuarantor) VisitStopped() error { maybeCloudUserId, maybeCloudInstanceId, guarantor.allowedCORSOrigins, + guarantor.shouldRunInDebugMode, ) } if engineLaunchErr != nil { diff --git a/cli/cli/helpers/engine_manager/engine_manager.go b/cli/cli/helpers/engine_manager/engine_manager.go index 9090c0b4a8..7717e88c82 100644 --- a/cli/cli/helpers/engine_manager/engine_manager.go +++ b/cli/cli/helpers/engine_manager/engine_manager.go @@ -33,6 +33,8 @@ const ( defaultEngineVersion = "" waitUntilEngineStoppedTries = 5 waitUntilEngineStoppedCoolOff = 5 * time.Second + + doNotStartTheEngineInDebugModeForDefaultVersion = false ) type EngineManager struct { @@ -194,6 +196,7 @@ func (manager *EngineManager) StartEngineIdempotentlyWithDefaultVersion(ctx cont poolSize, manager.enclaveEnvVars, manager.allowedCORSOrigins, + doNotStartTheEngineInDebugModeForDefaultVersion, ) // 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 @@ -205,7 +208,7 @@ func (manager *EngineManager) StartEngineIdempotentlyWithDefaultVersion(ctx cont } // StartEngineIdempotentlyWithCustomVersion Starts an engine if one doesn't exist already, and returns a client to it -func (manager *EngineManager) StartEngineIdempotentlyWithCustomVersion(ctx context.Context, engineImageVersionTag string, logLevel logrus.Level, poolSize uint8) (kurtosis_engine_rpc_api_bindings.EngineServiceClient, func() error, error) { +func (manager *EngineManager) StartEngineIdempotentlyWithCustomVersion(ctx context.Context, engineImageVersionTag string, logLevel logrus.Level, poolSize uint8, shouldStartInDebugMode bool) (kurtosis_engine_rpc_api_bindings.EngineServiceClient, func() error, error) { status, maybeHostMachinePortBinding, engineVersion, err := manager.GetEngineStatus(ctx) if err != nil { return nil, nil, stacktrace.Propagate(err, "An error occurred retrieving the Kurtosis engine status, which is necessary for creating a connection to the engine") @@ -226,6 +229,7 @@ func (manager *EngineManager) StartEngineIdempotentlyWithCustomVersion(ctx conte poolSize, manager.enclaveEnvVars, manager.allowedCORSOrigins, + shouldStartInDebugMode, ) engineClient, engineClientCloseFunc, err := manager.startEngineWithGuarantor(ctx, status, engineGuarantor) if err != nil { @@ -317,7 +321,7 @@ func (manager *EngineManager) StopEngineIdempotently(ctx context.Context) error // If no optionalVersionToUse is passed, then the new engine will take the default version, unless // restartEngineOnSameVersionIfAnyRunning is set to true in which case it will take the version of the currently // running engine -func (manager *EngineManager) RestartEngineIdempotently(ctx context.Context, logLevel logrus.Level, optionalVersionToUse string, restartEngineOnSameVersionIfAnyRunning bool, poolSize uint8) (kurtosis_engine_rpc_api_bindings.EngineServiceClient, func() error, error) { +func (manager *EngineManager) RestartEngineIdempotently(ctx context.Context, logLevel logrus.Level, optionalVersionToUse string, restartEngineOnSameVersionIfAnyRunning bool, poolSize uint8, shouldStartInDebugMode bool) (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 _, _, currentEngineVersion, err := manager.GetEngineStatus(ctx) @@ -342,7 +346,7 @@ func (manager *EngineManager) RestartEngineIdempotently(ctx context.Context, log var engineClientCloseFunc func() error var restartEngineErr error if versionOfNewEngine != defaultEngineVersion { - _, engineClientCloseFunc, restartEngineErr = manager.StartEngineIdempotentlyWithCustomVersion(ctx, versionOfNewEngine, logLevel, poolSize) + _, engineClientCloseFunc, restartEngineErr = manager.StartEngineIdempotentlyWithCustomVersion(ctx, versionOfNewEngine, logLevel, poolSize, shouldStartInDebugMode) } else { _, engineClientCloseFunc, restartEngineErr = manager.StartEngineIdempotentlyWithDefaultVersion(ctx, logLevel, poolSize) } diff --git a/cli/cli/scripts/debug-cli.sh b/cli/cli/scripts/debug-cli.sh index 8a2984dea4..2e5bb4d410 100755 --- a/cli/cli/scripts/debug-cli.sh +++ b/cli/cli/scripts/debug-cli.sh @@ -47,13 +47,26 @@ fi # The funky ${1+"${@}"} incantation is how you feed arguments exactly as-is to a child script in Bash # ${*} loses quoting and ${@} trips set -e if no arguments are passed, so this incantation says, "if and only if # ${1} exists, evaluate ${@}" -cli_arguments=${1+"${@}"} +cli_arguments_and_flags=${1+"${@}"} first_argument=$1 headless_val="true" if [ "${first_argument}" == "dlv-terminal" ]; then headless_val="false" # The CLI's arguments start from the second position - cli_arguments=${2+"${@:2}"} + cli_arguments_and_flags=${2+"${@:2}"} fi -dlv --listen="127.0.0.1:${CLI_DEBUG_SERVER_PORT}" --headless="${headless_val}" --api-version=2 --check-go-version=false --only-same-user=false exec "${cli_binary_filepath}" ${cli_arguments} +# Split between program arguments and flags +cli_arguments="" +cli_flags="" +for argument_or_flag in ${cli_arguments_and_flags} +do + # If it's a flag + if [[ ${argument_or_flag} == -* ]]; then + cli_flags="${cli_flags} ${argument_or_flag}" + else + cli_arguments="${cli_arguments} ${argument_or_flag}" + fi +done + +dlv --listen="127.0.0.1:${CLI_DEBUG_SERVER_PORT}" --headless="${headless_val}" --api-version=2 --check-go-version=false --only-same-user=false exec "${cli_binary_filepath}" ${cli_arguments} -- "--debug-mode" ${cli_flags} diff --git a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go index d5ac56df8e..ed9c68ee64 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go @@ -110,6 +110,7 @@ func (backend *DockerKurtosisBackend) CreateEngine( imageVersionTag string, grpcPortNum uint16, envVars map[string]string, + shouldStartInDebugMode bool, ) ( *engine.Engine, error, @@ -122,6 +123,7 @@ func (backend *DockerKurtosisBackend) CreateEngine( envVars, backend.dockerManager, backend.objAttrsProvider, + shouldStartInDebugMode, ) } diff --git a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/engine_functions/create_engine.go b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/engine_functions/create_engine.go index 3f33d55e4f..b4986f9d69 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/engine_functions/create_engine.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/engine_functions/create_engine.go @@ -27,6 +27,7 @@ const ( //TODO: pass this parameter enclaveManagerUIPort = 9711 enclaveManagerAPIPort = 8081 + engineDebugServerPort = 50102 // in ClI this is 50101 and 50103 for the APIC maxWaitForEngineAvailabilityRetries = 10 timeBetweenWaitForEngineAvailabilityRetries = 1 * time.Second logsStorageDirpath = "/var/log/kurtosis/" @@ -41,6 +42,7 @@ func CreateEngine( envVars map[string]string, dockerManager *docker_manager.DockerManager, objAttrsProvider object_attributes_provider.DockerObjectAttributesProvider, + shouldStartInDebugMode bool, ) ( *engine.Engine, error, @@ -212,6 +214,31 @@ func CreateEngine( restAPIDockerPort: docker_manager.NewManualPublishingSpec(engine.RESTAPIPortAddr), } + // Configure the debug port only if it's required + if shouldStartInDebugMode { + debugServerPortSpec, err := port_spec.NewPortSpec( + uint16(engineDebugServerPort), + consts.EngineTransportProtocol, + consts.HttpApplicationProtocol, + defaultWait, + ) + if err != nil { + return nil, stacktrace.Propagate( + err, + "An error occurred creating the Engine's debug server port spec object using number '%v' and protocol '%v'", + engineDebugServerPort, + consts.EngineTransportProtocol.String(), + ) + } + + debugServerDockerPort, err := shared_helpers.TransformPortSpecToDockerPort(debugServerPortSpec) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred transforming the debug server port spec to a Docker port") + } + + usedPorts[debugServerDockerPort] = docker_manager.NewManualPublishingSpec(uint16(engineDebugServerPort)) + } + bindMounts := map[string]string{ // Necessary so that the engine server can interact with the Docker engine consts.DockerSocketFilepath: consts.DockerSocketFilepath, @@ -237,7 +264,7 @@ func CreateEngine( labelStrs[labelKey.GetString()] = labelValue.GetString() } - createAndStartArgs := docker_manager.NewCreateAndStartContainerArgsBuilder( + createAndStartArgsBuilder := docker_manager.NewCreateAndStartContainerArgsBuilder( containerImageAndTag, engineAttrs.GetName().GetString(), targetNetworkId, @@ -251,7 +278,23 @@ func CreateEngine( usedPorts, ).WithLabels( labelStrs, - ).Build() + ) + + if shouldStartInDebugMode { + // Adding systrace capabilities when starting the debug server in the engine's container + capabilities := map[docker_manager.ContainerCapability]bool{ + docker_manager.SysPtrace: true, + } + createAndStartArgsBuilder.WithAddedCapabilities(capabilities) + + // Setting security for debugging the engine's container + securityOpts := map[docker_manager.ContainerSecurityOpt]bool{ + docker_manager.AppArmorUnconfined: true, + } + createAndStartArgsBuilder.WithSecurityOpts(securityOpts) + } + + createAndStartArgs := createAndStartArgsBuilder.Build() containerId, hostMachinePortBindings, err := dockerManager.CreateAndStartContainer(ctx, createAndStartArgs) if err != nil { diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/container_capabilities.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/container_capabilities.go index 61e7f3637c..f35f3ce284 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_manager/container_capabilities.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/container_capabilities.go @@ -6,7 +6,8 @@ package docker_manager const ( - NetAdmin ContainerCapability = "NET_ADMIN" + NetAdmin ContainerCapability = "NET_ADMIN" + SysPtrace ContainerCapability = "SYS_PTRACE" ) type ContainerCapability string diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/container_security_opt.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/container_security_opt.go new file mode 100644 index 0000000000..d5288c8d27 --- /dev/null +++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/container_security_opt.go @@ -0,0 +1,7 @@ +package docker_manager + +const ( + AppArmorUnconfined ContainerSecurityOpt = "apparmor=unconfined" +) + +type ContainerSecurityOpt string diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/create_and_start_container_args.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/create_and_start_container_args.go index 4bc25bf54e..b91e322e5d 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_manager/create_and_start_container_args.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/create_and_start_container_args.go @@ -18,6 +18,7 @@ type CreateAndStartContainerArgs struct { networkId string staticIp net.IP addedCapabilities map[ContainerCapability]bool + securityOpts map[ContainerSecurityOpt]bool networkMode DockerManagerNetworkMode usedPorts map[nat.Port]PortPublishSpec entrypointArgs []string @@ -47,6 +48,7 @@ type CreateAndStartContainerArgsBuilder struct { networkId string staticIp net.IP addedCapabilities map[ContainerCapability]bool + securityOpts map[ContainerSecurityOpt]bool networkMode DockerManagerNetworkMode usedPorts map[nat.Port]PortPublishSpec entrypointArgs []string @@ -83,6 +85,7 @@ func NewCreateAndStartContainerArgsBuilder(dockerImage string, name string, netw networkId: networkId, staticIp: nil, addedCapabilities: map[ContainerCapability]bool{}, + securityOpts: map[ContainerSecurityOpt]bool{}, networkMode: DefaultNetworkMode, usedPorts: map[nat.Port]PortPublishSpec{}, entrypointArgs: nil, @@ -114,6 +117,7 @@ func (builder *CreateAndStartContainerArgsBuilder) Build() *CreateAndStartContai networkId: builder.networkId, staticIp: builder.staticIp, addedCapabilities: builder.addedCapabilities, + securityOpts: builder.securityOpts, networkMode: builder.networkMode, usedPorts: builder.usedPorts, entrypointArgs: builder.entrypointArgs, @@ -160,6 +164,13 @@ func (builder *CreateAndStartContainerArgsBuilder) WithAddedCapabilities(capabil return builder } +// A "set" of security options to add to the container, corresponding to the --security-opt Docker flag +// For more info, see https://docs.docker.com/engine/reference/commandline/container_run/#security-opt +func (builder *CreateAndStartContainerArgsBuilder) WithSecurityOpts(securityOpts map[ContainerSecurityOpt]bool) *CreateAndStartContainerArgsBuilder { + builder.securityOpts = securityOpts + return builder +} + // When a non-empty string, sets the Docker --network flag to be this given string func (builder *CreateAndStartContainerArgsBuilder) WithNetworkMode(mode DockerManagerNetworkMode) *CreateAndStartContainerArgsBuilder { builder.networkMode = mode diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go index 75abbb375a..ad5e386806 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go @@ -578,6 +578,7 @@ func (manager *DockerManager) CreateAndStartContainer( } containerHostConfigPtr, err := manager.getContainerHostConfig( args.addedCapabilities, + args.securityOpts, args.networkMode, args.bindMounts, args.volumeMounts, @@ -1613,6 +1614,7 @@ Args: */ func (manager *DockerManager) getContainerHostConfig( addedCapabilities map[ContainerCapability]bool, + securityOpts map[ContainerSecurityOpt]bool, networkMode DockerManagerNetworkMode, bindMounts map[string]string, volumeMounts map[string]string, @@ -1677,6 +1679,12 @@ func (manager *DockerManager) getContainerHostConfig( addedCapabilitiesSlice = append(addedCapabilitiesSlice, capabilityStr) } + securityOptsSlice := []string{} + for securityOpt := range securityOpts { + securityOptStr := string(securityOpt) + securityOptsSlice = append(securityOptsSlice, securityOptStr) + } + extraHosts := []string{} if needsToAccessDockerHostMachine { // This explicit specification is necessary because in Docker-for-Linux, the magic "host.docker.internal" @@ -1778,7 +1786,7 @@ func (manager *DockerManager) getContainerHostConfig( Privileged: false, PublishAllPorts: false, ReadonlyRootfs: false, - SecurityOpt: nil, + SecurityOpt: securityOptsSlice, StorageOpt: nil, Tmpfs: nil, UTSMode: "", diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go index 67bccf3a47..ce6008782b 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go @@ -43,6 +43,7 @@ func CreateEngine( envVars map[string]string, kubernetesManager *kubernetes_manager.KubernetesManager, objAttrsProvider object_attributes_provider.KubernetesObjectAttributesProvider, + _ bool, //It's not required to add extra configuration in K8S for enabling the debug server ) ( *engine.Engine, error, diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go index 43e1c4b1fb..b9bd72017f 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go @@ -131,6 +131,7 @@ func (backend *KubernetesKurtosisBackend) CreateEngine( imageVersionTag string, grpcPortNum uint16, envVars map[string]string, + shouldStartInDebugMode bool, ) ( *engine.Engine, error, @@ -143,6 +144,7 @@ func (backend *KubernetesKurtosisBackend) CreateEngine( envVars, backend.kubernetesManager, backend.objAttrsProvider, + shouldStartInDebugMode, ) if err != nil { return nil, stacktrace.Propagate( diff --git a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go index 3adc276992..87d98da01a 100644 --- a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go @@ -52,6 +52,7 @@ func (backend *MetricsReportingKurtosisBackend) CreateEngine( imageVersionTag string, grpcPortNum uint16, envVars map[string]string, + shouldStartInDebugMode bool, ) (*engine.Engine, error) { result, err := backend.underlying.CreateEngine( ctx, @@ -59,9 +60,10 @@ func (backend *MetricsReportingKurtosisBackend) CreateEngine( imageVersionTag, grpcPortNum, envVars, + shouldStartInDebugMode, ) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred creating the engine using image '%v' with tag '%v'", imageOrgAndRepo, imageVersionTag) + return nil, stacktrace.Propagate(err, "An error occurred creating the engine using image '%v' with tag '%v' and debug mode '%v'", imageOrgAndRepo, imageVersionTag, shouldStartInDebugMode) } return result, nil } diff --git a/container-engine-lib/lib/backend_interface/kurtosis_backend.go b/container-engine-lib/lib/backend_interface/kurtosis_backend.go index 06da782443..d7b655d70b 100644 --- a/container-engine-lib/lib/backend_interface/kurtosis_backend.go +++ b/container-engine-lib/lib/backend_interface/kurtosis_backend.go @@ -46,6 +46,7 @@ type KurtosisBackend interface { imageVersionTag string, grpcPortNum uint16, envVars map[string]string, + shouldStartInDebugMode bool, ) ( *engine.Engine, error, diff --git a/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go b/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go index 128bb9473c..dd68bb93fd 100644 --- a/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.22.1. DO NOT EDIT. +// Code generated by mockery v2.40.1. DO NOT EDIT. package backend_interface @@ -52,6 +52,10 @@ func (_m *MockKurtosisBackend) EXPECT() *MockKurtosisBackend_Expecter { func (_m *MockKurtosisBackend) BuildImage(ctx context.Context, imageName string, imageBuildSpec *image_build_spec.ImageBuildSpec) (string, error) { ret := _m.Called(ctx, imageName, imageBuildSpec) + if len(ret) == 0 { + panic("no return value specified for BuildImage") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, *image_build_spec.ImageBuildSpec) (string, error)); ok { @@ -106,6 +110,10 @@ func (_c *MockKurtosisBackend_BuildImage_Call) RunAndReturn(run func(context.Con func (_m *MockKurtosisBackend) CopyFilesFromUserService(ctx context.Context, enclaveUuid enclave.EnclaveUUID, serviceUuid service.ServiceUUID, srcPathOnService string, output io.Writer) error { ret := _m.Called(ctx, enclaveUuid, serviceUuid, srcPathOnService, output) + if len(ret) == 0 { + panic("no return value specified for CopyFilesFromUserService") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, enclave.EnclaveUUID, service.ServiceUUID, string, io.Writer) error); ok { r0 = rf(ctx, enclaveUuid, serviceUuid, srcPathOnService, output) @@ -152,6 +160,10 @@ func (_c *MockKurtosisBackend_CopyFilesFromUserService_Call) RunAndReturn(run fu func (_m *MockKurtosisBackend) CreateAPIContainer(ctx context.Context, image string, enclaveUuid enclave.EnclaveUUID, grpcPortNum uint16, enclaveDataVolumeDirpath string, ownIpAddressEnvVar string, customEnvVars map[string]string) (*api_container.APIContainer, error) { ret := _m.Called(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars) + if len(ret) == 0 { + panic("no return value specified for CreateAPIContainer") + } + var r0 *api_container.APIContainer var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string) (*api_container.APIContainer, error)); ok { @@ -212,6 +224,10 @@ func (_c *MockKurtosisBackend_CreateAPIContainer_Call) RunAndReturn(run func(con func (_m *MockKurtosisBackend) CreateEnclave(ctx context.Context, enclaveUuid enclave.EnclaveUUID, enclaveName string) (*enclave.Enclave, error) { ret := _m.Called(ctx, enclaveUuid, enclaveName) + if len(ret) == 0 { + panic("no return value specified for CreateEnclave") + } + var r0 *enclave.Enclave var r1 error if rf, ok := ret.Get(0).(func(context.Context, enclave.EnclaveUUID, string) (*enclave.Enclave, error)); ok { @@ -264,25 +280,29 @@ func (_c *MockKurtosisBackend_CreateEnclave_Call) RunAndReturn(run func(context. return _c } -// CreateEngine provides a mock function with given fields: ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars -func (_m *MockKurtosisBackend) CreateEngine(ctx context.Context, imageOrgAndRepo string, imageVersionTag string, grpcPortNum uint16, envVars map[string]string) (*engine.Engine, error) { - ret := _m.Called(ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars) +// CreateEngine provides a mock function with given fields: ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars, shouldStartInDebugMode +func (_m *MockKurtosisBackend) CreateEngine(ctx context.Context, imageOrgAndRepo string, imageVersionTag string, grpcPortNum uint16, envVars map[string]string, shouldStartInDebugMode bool) (*engine.Engine, error) { + ret := _m.Called(ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars, shouldStartInDebugMode) + + if len(ret) == 0 { + panic("no return value specified for CreateEngine") + } var r0 *engine.Engine var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, uint16, map[string]string) (*engine.Engine, error)); ok { - return rf(ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars) + if rf, ok := ret.Get(0).(func(context.Context, string, string, uint16, map[string]string, bool) (*engine.Engine, error)); ok { + return rf(ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars, shouldStartInDebugMode) } - if rf, ok := ret.Get(0).(func(context.Context, string, string, uint16, map[string]string) *engine.Engine); ok { - r0 = rf(ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars) + if rf, ok := ret.Get(0).(func(context.Context, string, string, uint16, map[string]string, bool) *engine.Engine); ok { + r0 = rf(ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars, shouldStartInDebugMode) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*engine.Engine) } } - if rf, ok := ret.Get(1).(func(context.Context, string, string, uint16, map[string]string) error); ok { - r1 = rf(ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars) + if rf, ok := ret.Get(1).(func(context.Context, string, string, uint16, map[string]string, bool) error); ok { + r1 = rf(ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars, shouldStartInDebugMode) } else { r1 = ret.Error(1) } @@ -301,13 +321,14 @@ type MockKurtosisBackend_CreateEngine_Call struct { // - imageVersionTag string // - grpcPortNum uint16 // - envVars map[string]string -func (_e *MockKurtosisBackend_Expecter) CreateEngine(ctx interface{}, imageOrgAndRepo interface{}, imageVersionTag interface{}, grpcPortNum interface{}, envVars interface{}) *MockKurtosisBackend_CreateEngine_Call { - return &MockKurtosisBackend_CreateEngine_Call{Call: _e.mock.On("CreateEngine", ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars)} +// - shouldStartInDebugMode bool +func (_e *MockKurtosisBackend_Expecter) CreateEngine(ctx interface{}, imageOrgAndRepo interface{}, imageVersionTag interface{}, grpcPortNum interface{}, envVars interface{}, shouldStartInDebugMode interface{}) *MockKurtosisBackend_CreateEngine_Call { + return &MockKurtosisBackend_CreateEngine_Call{Call: _e.mock.On("CreateEngine", ctx, imageOrgAndRepo, imageVersionTag, grpcPortNum, envVars, shouldStartInDebugMode)} } -func (_c *MockKurtosisBackend_CreateEngine_Call) Run(run func(ctx context.Context, imageOrgAndRepo string, imageVersionTag string, grpcPortNum uint16, envVars map[string]string)) *MockKurtosisBackend_CreateEngine_Call { +func (_c *MockKurtosisBackend_CreateEngine_Call) Run(run func(ctx context.Context, imageOrgAndRepo string, imageVersionTag string, grpcPortNum uint16, envVars map[string]string, shouldStartInDebugMode bool)) *MockKurtosisBackend_CreateEngine_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(uint16), args[4].(map[string]string)) + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(uint16), args[4].(map[string]string), args[5].(bool)) }) return _c } @@ -317,7 +338,7 @@ func (_c *MockKurtosisBackend_CreateEngine_Call) Return(_a0 *engine.Engine, _a1 return _c } -func (_c *MockKurtosisBackend_CreateEngine_Call) RunAndReturn(run func(context.Context, string, string, uint16, map[string]string) (*engine.Engine, error)) *MockKurtosisBackend_CreateEngine_Call { +func (_c *MockKurtosisBackend_CreateEngine_Call) RunAndReturn(run func(context.Context, string, string, uint16, map[string]string, bool) (*engine.Engine, error)) *MockKurtosisBackend_CreateEngine_Call { _c.Call.Return(run) return _c } @@ -326,6 +347,10 @@ func (_c *MockKurtosisBackend_CreateEngine_Call) RunAndReturn(run func(context.C func (_m *MockKurtosisBackend) CreateLogsAggregator(ctx context.Context) (*logs_aggregator.LogsAggregator, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for CreateLogsAggregator") + } + var r0 *logs_aggregator.LogsAggregator var r1 error if rf, ok := ret.Get(0).(func(context.Context) (*logs_aggregator.LogsAggregator, error)); ok { @@ -380,6 +405,10 @@ func (_c *MockKurtosisBackend_CreateLogsAggregator_Call) RunAndReturn(run func(c func (_m *MockKurtosisBackend) CreateLogsCollectorForEnclave(ctx context.Context, enclaveUuid enclave.EnclaveUUID, logsCollectorHttpPortNumber uint16, logsCollectorTcpPortNumber uint16) (*logs_collector.LogsCollector, error) { ret := _m.Called(ctx, enclaveUuid, logsCollectorHttpPortNumber, logsCollectorTcpPortNumber) + if len(ret) == 0 { + panic("no return value specified for CreateLogsCollectorForEnclave") + } + var r0 *logs_collector.LogsCollector var r1 error if rf, ok := ret.Get(0).(func(context.Context, enclave.EnclaveUUID, uint16, uint16) (*logs_collector.LogsCollector, error)); ok { @@ -437,6 +466,10 @@ func (_c *MockKurtosisBackend_CreateLogsCollectorForEnclave_Call) RunAndReturn(r func (_m *MockKurtosisBackend) CreateReverseProxy(ctx context.Context, engineGuid engine.EngineGUID) (*reverse_proxy.ReverseProxy, error) { ret := _m.Called(ctx, engineGuid) + if len(ret) == 0 { + panic("no return value specified for CreateReverseProxy") + } + var r0 *reverse_proxy.ReverseProxy var r1 error if rf, ok := ret.Get(0).(func(context.Context, engine.EngineGUID) (*reverse_proxy.ReverseProxy, error)); ok { @@ -492,6 +525,10 @@ func (_c *MockKurtosisBackend_CreateReverseProxy_Call) RunAndReturn(run func(con func (_m *MockKurtosisBackend) DestroyAPIContainers(ctx context.Context, filters *api_container.APIContainerFilters) (map[enclave.EnclaveUUID]bool, map[enclave.EnclaveUUID]error, error) { ret := _m.Called(ctx, filters) + if len(ret) == 0 { + panic("no return value specified for DestroyAPIContainers") + } + var r0 map[enclave.EnclaveUUID]bool var r1 map[enclave.EnclaveUUID]error var r2 error @@ -556,6 +593,10 @@ func (_c *MockKurtosisBackend_DestroyAPIContainers_Call) RunAndReturn(run func(c func (_m *MockKurtosisBackend) DestroyEnclaves(ctx context.Context, filters *enclave.EnclaveFilters) (map[enclave.EnclaveUUID]bool, map[enclave.EnclaveUUID]error, error) { ret := _m.Called(ctx, filters) + if len(ret) == 0 { + panic("no return value specified for DestroyEnclaves") + } + var r0 map[enclave.EnclaveUUID]bool var r1 map[enclave.EnclaveUUID]error var r2 error @@ -620,6 +661,10 @@ func (_c *MockKurtosisBackend_DestroyEnclaves_Call) RunAndReturn(run func(contex func (_m *MockKurtosisBackend) DestroyEngines(ctx context.Context, filters *engine.EngineFilters) (map[engine.EngineGUID]bool, map[engine.EngineGUID]error, error) { ret := _m.Called(ctx, filters) + if len(ret) == 0 { + panic("no return value specified for DestroyEngines") + } + var r0 map[engine.EngineGUID]bool var r1 map[engine.EngineGUID]error var r2 error @@ -684,6 +729,10 @@ func (_c *MockKurtosisBackend_DestroyEngines_Call) RunAndReturn(run func(context func (_m *MockKurtosisBackend) DestroyLogsAggregator(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for DestroyLogsAggregator") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -726,6 +775,10 @@ func (_c *MockKurtosisBackend_DestroyLogsAggregator_Call) RunAndReturn(run func( func (_m *MockKurtosisBackend) DestroyLogsCollectorForEnclave(ctx context.Context, enclaveUuid enclave.EnclaveUUID) error { ret := _m.Called(ctx, enclaveUuid) + if len(ret) == 0 { + panic("no return value specified for DestroyLogsCollectorForEnclave") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, enclave.EnclaveUUID) error); ok { r0 = rf(ctx, enclaveUuid) @@ -769,6 +822,10 @@ func (_c *MockKurtosisBackend_DestroyLogsCollectorForEnclave_Call) RunAndReturn( func (_m *MockKurtosisBackend) DestroyReverseProxy(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for DestroyReverseProxy") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -811,6 +868,10 @@ func (_c *MockKurtosisBackend_DestroyReverseProxy_Call) RunAndReturn(run func(co func (_m *MockKurtosisBackend) DestroyUserServices(ctx context.Context, enclaveUuid enclave.EnclaveUUID, filters *service.ServiceFilters) (map[service.ServiceUUID]bool, map[service.ServiceUUID]error, error) { ret := _m.Called(ctx, enclaveUuid, filters) + if len(ret) == 0 { + panic("no return value specified for DestroyUserServices") + } + var r0 map[service.ServiceUUID]bool var r1 map[service.ServiceUUID]error var r2 error @@ -876,6 +937,10 @@ func (_c *MockKurtosisBackend_DestroyUserServices_Call) RunAndReturn(run func(co func (_m *MockKurtosisBackend) DumpEnclave(ctx context.Context, enclaveUuid enclave.EnclaveUUID, outputDirpath string) error { ret := _m.Called(ctx, enclaveUuid, outputDirpath) + if len(ret) == 0 { + panic("no return value specified for DumpEnclave") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, enclave.EnclaveUUID, string) error); ok { r0 = rf(ctx, enclaveUuid, outputDirpath) @@ -920,6 +985,10 @@ func (_c *MockKurtosisBackend_DumpEnclave_Call) RunAndReturn(run func(context.Co func (_m *MockKurtosisBackend) DumpKurtosis(ctx context.Context, outputDirpath string) error { ret := _m.Called(ctx, outputDirpath) + if len(ret) == 0 { + panic("no return value specified for DumpKurtosis") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, outputDirpath) @@ -963,6 +1032,10 @@ func (_c *MockKurtosisBackend_DumpKurtosis_Call) RunAndReturn(run func(context.C func (_m *MockKurtosisBackend) FetchImage(ctx context.Context, image string, registrySpec *image_registry_spec.ImageRegistrySpec, downloadMode image_download_mode.ImageDownloadMode) (bool, string, error) { ret := _m.Called(ctx, image, registrySpec, downloadMode) + if len(ret) == 0 { + panic("no return value specified for FetchImage") + } + var r0 bool var r1 string var r2 error @@ -1025,6 +1098,10 @@ func (_c *MockKurtosisBackend_FetchImage_Call) RunAndReturn(run func(context.Con func (_m *MockKurtosisBackend) GetAPIContainers(ctx context.Context, filters *api_container.APIContainerFilters) (map[enclave.EnclaveUUID]*api_container.APIContainer, error) { ret := _m.Called(ctx, filters) + if len(ret) == 0 { + panic("no return value specified for GetAPIContainers") + } + var r0 map[enclave.EnclaveUUID]*api_container.APIContainer var r1 error if rf, ok := ret.Get(0).(func(context.Context, *api_container.APIContainerFilters) (map[enclave.EnclaveUUID]*api_container.APIContainer, error)); ok { @@ -1080,6 +1157,10 @@ func (_c *MockKurtosisBackend_GetAPIContainers_Call) RunAndReturn(run func(conte func (_m *MockKurtosisBackend) GetAvailableCPUAndMemory(ctx context.Context) (compute_resources.MemoryInMegaBytes, compute_resources.CpuMilliCores, bool, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAvailableCPUAndMemory") + } + var r0 compute_resources.MemoryInMegaBytes var r1 compute_resources.CpuMilliCores var r2 bool @@ -1146,6 +1227,10 @@ func (_c *MockKurtosisBackend_GetAvailableCPUAndMemory_Call) RunAndReturn(run fu func (_m *MockKurtosisBackend) GetEnclaves(ctx context.Context, filters *enclave.EnclaveFilters) (map[enclave.EnclaveUUID]*enclave.Enclave, error) { ret := _m.Called(ctx, filters) + if len(ret) == 0 { + panic("no return value specified for GetEnclaves") + } + var r0 map[enclave.EnclaveUUID]*enclave.Enclave var r1 error if rf, ok := ret.Get(0).(func(context.Context, *enclave.EnclaveFilters) (map[enclave.EnclaveUUID]*enclave.Enclave, error)); ok { @@ -1201,6 +1286,10 @@ func (_c *MockKurtosisBackend_GetEnclaves_Call) RunAndReturn(run func(context.Co func (_m *MockKurtosisBackend) GetEngineLogs(ctx context.Context, outputDirpath string) error { ret := _m.Called(ctx, outputDirpath) + if len(ret) == 0 { + panic("no return value specified for GetEngineLogs") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, outputDirpath) @@ -1244,6 +1333,10 @@ func (_c *MockKurtosisBackend_GetEngineLogs_Call) RunAndReturn(run func(context. func (_m *MockKurtosisBackend) GetEngines(ctx context.Context, filters *engine.EngineFilters) (map[engine.EngineGUID]*engine.Engine, error) { ret := _m.Called(ctx, filters) + if len(ret) == 0 { + panic("no return value specified for GetEngines") + } + var r0 map[engine.EngineGUID]*engine.Engine var r1 error if rf, ok := ret.Get(0).(func(context.Context, *engine.EngineFilters) (map[engine.EngineGUID]*engine.Engine, error)); ok { @@ -1299,6 +1392,10 @@ func (_c *MockKurtosisBackend_GetEngines_Call) RunAndReturn(run func(context.Con func (_m *MockKurtosisBackend) GetLogsAggregator(ctx context.Context) (*logs_aggregator.LogsAggregator, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetLogsAggregator") + } + var r0 *logs_aggregator.LogsAggregator var r1 error if rf, ok := ret.Get(0).(func(context.Context) (*logs_aggregator.LogsAggregator, error)); ok { @@ -1353,6 +1450,10 @@ func (_c *MockKurtosisBackend_GetLogsAggregator_Call) RunAndReturn(run func(cont func (_m *MockKurtosisBackend) GetLogsCollectorForEnclave(ctx context.Context, enclaveUuid enclave.EnclaveUUID) (*logs_collector.LogsCollector, error) { ret := _m.Called(ctx, enclaveUuid) + if len(ret) == 0 { + panic("no return value specified for GetLogsCollectorForEnclave") + } + var r0 *logs_collector.LogsCollector var r1 error if rf, ok := ret.Get(0).(func(context.Context, enclave.EnclaveUUID) (*logs_collector.LogsCollector, error)); ok { @@ -1408,6 +1509,10 @@ func (_c *MockKurtosisBackend_GetLogsCollectorForEnclave_Call) RunAndReturn(run func (_m *MockKurtosisBackend) GetReverseProxy(ctx context.Context) (*reverse_proxy.ReverseProxy, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetReverseProxy") + } + var r0 *reverse_proxy.ReverseProxy var r1 error if rf, ok := ret.Get(0).(func(context.Context) (*reverse_proxy.ReverseProxy, error)); ok { @@ -1462,6 +1567,10 @@ func (_c *MockKurtosisBackend_GetReverseProxy_Call) RunAndReturn(run func(contex func (_m *MockKurtosisBackend) GetShellOnUserService(ctx context.Context, enclaveUuid enclave.EnclaveUUID, serviceUuid service.ServiceUUID) error { ret := _m.Called(ctx, enclaveUuid, serviceUuid) + if len(ret) == 0 { + panic("no return value specified for GetShellOnUserService") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, enclave.EnclaveUUID, service.ServiceUUID) error); ok { r0 = rf(ctx, enclaveUuid, serviceUuid) @@ -1506,6 +1615,10 @@ func (_c *MockKurtosisBackend_GetShellOnUserService_Call) RunAndReturn(run func( func (_m *MockKurtosisBackend) GetUserServiceLogs(ctx context.Context, enclaveUuid enclave.EnclaveUUID, filters *service.ServiceFilters, shouldFollowLogs bool) (map[service.ServiceUUID]io.ReadCloser, map[service.ServiceUUID]error, error) { ret := _m.Called(ctx, enclaveUuid, filters, shouldFollowLogs) + if len(ret) == 0 { + panic("no return value specified for GetUserServiceLogs") + } + var r0 map[service.ServiceUUID]io.ReadCloser var r1 map[service.ServiceUUID]error var r2 error @@ -1572,6 +1685,10 @@ func (_c *MockKurtosisBackend_GetUserServiceLogs_Call) RunAndReturn(run func(con func (_m *MockKurtosisBackend) GetUserServices(ctx context.Context, enclaveUuid enclave.EnclaveUUID, filters *service.ServiceFilters) (map[service.ServiceUUID]*service.Service, error) { ret := _m.Called(ctx, enclaveUuid, filters) + if len(ret) == 0 { + panic("no return value specified for GetUserServices") + } + var r0 map[service.ServiceUUID]*service.Service var r1 error if rf, ok := ret.Get(0).(func(context.Context, enclave.EnclaveUUID, *service.ServiceFilters) (map[service.ServiceUUID]*service.Service, error)); ok { @@ -1628,6 +1745,10 @@ func (_c *MockKurtosisBackend_GetUserServices_Call) RunAndReturn(run func(contex func (_m *MockKurtosisBackend) PruneUnusedImages(ctx context.Context) ([]string, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for PruneUnusedImages") + } + var r0 []string var r1 error if rf, ok := ret.Get(0).(func(context.Context) ([]string, error)); ok { @@ -1682,6 +1803,10 @@ func (_c *MockKurtosisBackend_PruneUnusedImages_Call) RunAndReturn(run func(cont func (_m *MockKurtosisBackend) RegisterUserServices(ctx context.Context, enclaveUuid enclave.EnclaveUUID, services map[service.ServiceName]bool) (map[service.ServiceName]*service.ServiceRegistration, map[service.ServiceName]error, error) { ret := _m.Called(ctx, enclaveUuid, services) + if len(ret) == 0 { + panic("no return value specified for RegisterUserServices") + } + var r0 map[service.ServiceName]*service.ServiceRegistration var r1 map[service.ServiceName]error var r2 error @@ -1747,6 +1872,10 @@ func (_c *MockKurtosisBackend_RegisterUserServices_Call) RunAndReturn(run func(c func (_m *MockKurtosisBackend) RemoveRegisteredUserServiceProcesses(ctx context.Context, enclaveUuid enclave.EnclaveUUID, services map[service.ServiceUUID]bool) (map[service.ServiceUUID]bool, map[service.ServiceUUID]error, error) { ret := _m.Called(ctx, enclaveUuid, services) + if len(ret) == 0 { + panic("no return value specified for RemoveRegisteredUserServiceProcesses") + } + var r0 map[service.ServiceUUID]bool var r1 map[service.ServiceUUID]error var r2 error @@ -1812,6 +1941,10 @@ func (_c *MockKurtosisBackend_RemoveRegisteredUserServiceProcesses_Call) RunAndR func (_m *MockKurtosisBackend) RunUserServiceExecCommandWithStreamedOutput(ctx context.Context, enclaveUuid enclave.EnclaveUUID, serviceUuid service.ServiceUUID, cmd []string) (chan string, chan *exec_result.ExecResult, error) { ret := _m.Called(ctx, enclaveUuid, serviceUuid, cmd) + if len(ret) == 0 { + panic("no return value specified for RunUserServiceExecCommandWithStreamedOutput") + } + var r0 chan string var r1 chan *exec_result.ExecResult var r2 error @@ -1878,6 +2011,10 @@ func (_c *MockKurtosisBackend_RunUserServiceExecCommandWithStreamedOutput_Call) func (_m *MockKurtosisBackend) RunUserServiceExecCommands(ctx context.Context, enclaveUuid enclave.EnclaveUUID, userServiceCommands map[service.ServiceUUID][]string) (map[service.ServiceUUID]*exec_result.ExecResult, map[service.ServiceUUID]error, error) { ret := _m.Called(ctx, enclaveUuid, userServiceCommands) + if len(ret) == 0 { + panic("no return value specified for RunUserServiceExecCommands") + } + var r0 map[service.ServiceUUID]*exec_result.ExecResult var r1 map[service.ServiceUUID]error var r2 error @@ -1943,6 +2080,10 @@ func (_c *MockKurtosisBackend_RunUserServiceExecCommands_Call) RunAndReturn(run func (_m *MockKurtosisBackend) StartRegisteredUserServices(ctx context.Context, enclaveUuid enclave.EnclaveUUID, services map[service.ServiceUUID]*service.ServiceConfig) (map[service.ServiceUUID]*service.Service, map[service.ServiceUUID]error, error) { ret := _m.Called(ctx, enclaveUuid, services) + if len(ret) == 0 { + panic("no return value specified for StartRegisteredUserServices") + } + var r0 map[service.ServiceUUID]*service.Service var r1 map[service.ServiceUUID]error var r2 error @@ -2008,6 +2149,10 @@ func (_c *MockKurtosisBackend_StartRegisteredUserServices_Call) RunAndReturn(run func (_m *MockKurtosisBackend) StopAPIContainers(ctx context.Context, filters *api_container.APIContainerFilters) (map[enclave.EnclaveUUID]bool, map[enclave.EnclaveUUID]error, error) { ret := _m.Called(ctx, filters) + if len(ret) == 0 { + panic("no return value specified for StopAPIContainers") + } + var r0 map[enclave.EnclaveUUID]bool var r1 map[enclave.EnclaveUUID]error var r2 error @@ -2072,6 +2217,10 @@ func (_c *MockKurtosisBackend_StopAPIContainers_Call) RunAndReturn(run func(cont func (_m *MockKurtosisBackend) StopEnclaves(ctx context.Context, filters *enclave.EnclaveFilters) (map[enclave.EnclaveUUID]bool, map[enclave.EnclaveUUID]error, error) { ret := _m.Called(ctx, filters) + if len(ret) == 0 { + panic("no return value specified for StopEnclaves") + } + var r0 map[enclave.EnclaveUUID]bool var r1 map[enclave.EnclaveUUID]error var r2 error @@ -2136,6 +2285,10 @@ func (_c *MockKurtosisBackend_StopEnclaves_Call) RunAndReturn(run func(context.C func (_m *MockKurtosisBackend) StopEngines(ctx context.Context, filters *engine.EngineFilters) (map[engine.EngineGUID]bool, map[engine.EngineGUID]error, error) { ret := _m.Called(ctx, filters) + if len(ret) == 0 { + panic("no return value specified for StopEngines") + } + var r0 map[engine.EngineGUID]bool var r1 map[engine.EngineGUID]error var r2 error @@ -2200,6 +2353,10 @@ func (_c *MockKurtosisBackend_StopEngines_Call) RunAndReturn(run func(context.Co func (_m *MockKurtosisBackend) StopUserServices(ctx context.Context, enclaveUuid enclave.EnclaveUUID, filters *service.ServiceFilters) (map[service.ServiceUUID]bool, map[service.ServiceUUID]error, error) { ret := _m.Called(ctx, enclaveUuid, filters) + if len(ret) == 0 { + panic("no return value specified for StopUserServices") + } + var r0 map[service.ServiceUUID]bool var r1 map[service.ServiceUUID]error var r2 error @@ -2265,6 +2422,10 @@ func (_c *MockKurtosisBackend_StopUserServices_Call) RunAndReturn(run func(conte func (_m *MockKurtosisBackend) UnregisterUserServices(ctx context.Context, enclaveUuid enclave.EnclaveUUID, services map[service.ServiceUUID]bool) (map[service.ServiceUUID]bool, map[service.ServiceUUID]error, error) { ret := _m.Called(ctx, enclaveUuid, services) + if len(ret) == 0 { + panic("no return value specified for UnregisterUserServices") + } + var r0 map[service.ServiceUUID]bool var r1 map[service.ServiceUUID]error var r2 error @@ -2330,6 +2491,10 @@ func (_c *MockKurtosisBackend_UnregisterUserServices_Call) RunAndReturn(run func func (_m *MockKurtosisBackend) UpdateEnclave(ctx context.Context, enclaveUuid enclave.EnclaveUUID, newName string, creationTime *time.Time) error { ret := _m.Called(ctx, enclaveUuid, newName, creationTime) + if len(ret) == 0 { + panic("no return value specified for UpdateEnclave") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, enclave.EnclaveUUID, string, *time.Time) error); ok { r0 = rf(ctx, enclaveUuid, newName, creationTime) @@ -2371,13 +2536,12 @@ func (_c *MockKurtosisBackend_UpdateEnclave_Call) RunAndReturn(run func(context. return _c } -type mockConstructorTestingTNewMockKurtosisBackend interface { +// NewMockKurtosisBackend creates a new instance of MockKurtosisBackend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockKurtosisBackend(t interface { mock.TestingT Cleanup(func()) -} - -// NewMockKurtosisBackend creates a new instance of MockKurtosisBackend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMockKurtosisBackend(t mockConstructorTestingTNewMockKurtosisBackend) *MockKurtosisBackend { +}) *MockKurtosisBackend { mock := &MockKurtosisBackend{} mock.Mock.Test(t) diff --git a/engine/launcher/engine_server_launcher/engine_server_launcher.go b/engine/launcher/engine_server_launcher/engine_server_launcher.go index 2a302c5c2a..664dd11f2a 100644 --- a/engine/launcher/engine_server_launcher/engine_server_launcher.go +++ b/engine/launcher/engine_server_launcher/engine_server_launcher.go @@ -7,8 +7,6 @@ package engine_server_launcher import ( "context" - "net" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/engine/launcher/args" @@ -16,6 +14,7 @@ import ( "github.com/kurtosis-tech/kurtosis/metrics-library/golang/lib/metrics_client" "github.com/kurtosis-tech/stacktrace" "github.com/sirupsen/logrus" + "net" ) const ( @@ -45,6 +44,7 @@ func (launcher *EngineServerLauncher) LaunchWithDefaultVersion( cloudUserID metrics_client.CloudUserID, cloudInstanceID metrics_client.CloudInstanceID, allowedCORSOrigins *[]string, + shouldStartInDebugMode bool, ) ( resultPublicIpAddr net.IP, resultPublicGrpcPortSpec *port_spec.PortSpec, @@ -65,6 +65,7 @@ func (launcher *EngineServerLauncher) LaunchWithDefaultVersion( cloudUserID, cloudInstanceID, allowedCORSOrigins, + shouldStartInDebugMode, ) 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) @@ -87,6 +88,7 @@ func (launcher *EngineServerLauncher) LaunchWithCustomVersion( cloudUserID metrics_client.CloudUserID, cloudInstanceID metrics_client.CloudInstanceID, allowedCORSOrigins *[]string, + shouldStartInDebugMode bool, ) ( resultPublicIpAddr net.IP, resultPublicGrpcPortSpec *port_spec.PortSpec, @@ -124,6 +126,7 @@ func (launcher *EngineServerLauncher) LaunchWithCustomVersion( imageVersionTag, grpcListenPortNum, envVars, + shouldStartInDebugMode, ) if err != nil { return nil, nil, stacktrace.Propagate(err, "An error occurred launching the engine server container with environment variables '%+v'", envVars) diff --git a/engine/scripts/build.sh b/engine/scripts/build.sh index 3c51b72bb1..b1c43e013b 100755 --- a/engine/scripts/build.sh +++ b/engine/scripts/build.sh @@ -10,18 +10,39 @@ root_dirpath="$(dirname "${script_dirpath}")" # ================================================================================================== # Constants # ================================================================================================== +DEFAULT_DEBUG_IMAGE=false +SET_ARCH_AUTOMATICALLY="" +DEFAULT_SKIP_DOCKER_IMAGE_BUILDING=false + BUILD_SCRIPT_RELATIVE_FILEPATHS=( "launcher/scripts/build.sh" "server/scripts/build.sh" ) +# ================================================================================================== +# Arg Parsing & Validation +# ================================================================================================== +show_helptext_and_exit() { + echo "Usage: $(basename "${0}") debug_image..." + echo "" + echo " debug_image Whether images should contains the debug server and run in debug mode, this will use the Dockerfile.debug image to build the container (only configured for the engine server so far)" + echo "" + exit 1 # Exit with an error so that if this is accidentally called by CI, the script will fail +} + +# Raw parsing of the arguments +debug_image="${1:-"${DEFAULT_DEBUG_IMAGE}"}" +if [ "${debug_image}" != "true" ] && [ "${debug_image}" != "false" ]; then + echo "Error: Invalid debug_image arg: '${debug_image}'" >&2 + show_helptext_and_exit +fi # ================================================================================================== # Main Logic # ================================================================================================== for build_script_rel_filepath in "${BUILD_SCRIPT_RELATIVE_FILEPATHS[@]}"; do build_script_abs_filepath="${root_dirpath}/${build_script_rel_filepath}" - if ! bash "${build_script_abs_filepath}"; then + if ! bash "${build_script_abs_filepath}" "${DEFAULT_SKIP_DOCKER_IMAGE_BUILDING}" "${SET_ARCH_AUTOMATICALLY}" "${debug_image}"; then echo "Error: Build script '${build_script_abs_filepath}' failed" >&2 exit 1 fi diff --git a/engine/server/.dockerignore b/engine/server/.dockerignore index 36463a2a9f..1b9d74f022 100644 --- a/engine/server/.dockerignore +++ b/engine/server/.dockerignore @@ -13,3 +13,4 @@ scripts .dockerignore Dockerfile +Dockerfile.debug diff --git a/engine/server/Dockerfile.debug b/engine/server/Dockerfile.debug new file mode 100644 index 0000000000..0457d231e8 --- /dev/null +++ b/engine/server/Dockerfile.debug @@ -0,0 +1,21 @@ +# Final stage +FROM alpine:3.19 + +RUN apk update && apk add bash + +# Make sure that you changed the port inside the engine's code before changing it here +EXPOSE 50102 + +ARG TARGETARCH + +WORKDIR /run + +ADD ./webapp ./webapp + +COPY ./build/kurtosis-engine.$TARGETARCH ./kurtosis-engine + +COPY ./build/dlv.$TARGETARCH /dlv + +# Make sure that you changed the port inside the engine's code before changing it here +CMD ["/dlv", "--listen=:50102", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "--continue", "./kurtosis-engine"] + diff --git a/engine/server/scripts/_constants.env b/engine/server/scripts/_constants.env index 381f1e7059..d1917467bb 100644 --- a/engine/server/scripts/_constants.env +++ b/engine/server/scripts/_constants.env @@ -3,3 +3,13 @@ # constant in the 'launcher' submodule!! IMAGE_ORG_AND_REPO="kurtosistech/engine" # ^^^^^^^^^^^^^^^^ WARNING ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +BUILD_DIRNAME="build" +DEFAULT_SKIP_DOCKER_IMAGE_BUILDING=false +DEFAULT_ARCHITECTURE_TO_BUILD="unknown" +DELVE_SERVER_REPOSITORY_AND_VERSION="github.com/go-delve/delve/cmd/dlv@latest" +DOCKER_IMAGE_FILENAME="Dockerfile" +DOCKER_DEBUG_IMAGE_FILENAME="${DOCKER_IMAGE_FILENAME}.debug" +DOCKER_DEBUG_IMAGE_NAME_SUFFIX="debug" +DEFAULT_DEBUG_IMAGE=false + diff --git a/engine/server/scripts/build.sh b/engine/server/scripts/build.sh index 05655c1c4f..bb9a5c3c12 100755 --- a/engine/server/scripts/build.sh +++ b/engine/server/scripts/build.sh @@ -11,12 +11,6 @@ uname_arch=$(uname -m) # ================================================================================================== source "${script_dirpath}/_constants.env" -BUILD_DIRNAME="build" - -DEFAULT_SKIP_DOCKER_IMAGE_BUILDING=false - -DEFAULT_ARCHITECTURE_TO_BUILD="unknown" - if [ "$uname_arch" == "x86_64" ] || [ "$uname_arch" == "amd64" ]; then DEFAULT_ARCHITECTURE_TO_BUILD="amd64" elif [ "$uname_arch" == "aarch64" ] || [ "$uname_arch" == "arm64" ]; then @@ -28,19 +22,45 @@ MAIN_GO_FILEPATH="${engine_root_dirpath}/${MAIN_DIRNAME}/main.go" MAIN_BINARY_OUTPUT_FILENAME="kurtosis-engine" MAIN_BINARY_OUTPUT_FILEPATH="${engine_root_dirpath}/${BUILD_DIRNAME}/${MAIN_BINARY_OUTPUT_FILENAME}" -# ============================================================================= -# Main Code -# ============================================================================= +DELVE_BINARY_FILENAME="dlv" +DELVE_BINARY_OUTPUT_FILEPATH="${engine_root_dirpath}/${BUILD_DIRNAME}/${DELVE_BINARY_FILENAME}" + +# ================================================================================================== +# Arg Parsing & Validation +# ================================================================================================== +show_helptext_and_exit() { + echo "Usage: $(basename "${0}") skip_docker_image_building, architecture_to_build, debug_image..." + echo "" + echo " skip_docker_image_building Whether build the Docker image" + echo " architecture_to_build The desired architecture for the project's binaries" + echo " debug_image Whether images should contains the debug server and run in debug mode, this will use the Dockerfile.debug image to build the container" + echo "" + exit 1 # Exit with an error so that if this is accidentally called by CI, the script will fail +} + +# Raw parsing of the arguments skip_docker_image_building="${1:-"${DEFAULT_SKIP_DOCKER_IMAGE_BUILDING}"}" if [ "${skip_docker_image_building}" != "true" ] && [ "${skip_docker_image_building}" != "false" ]; then echo "Error: Invalid skip-docker-image-building arg '${skip_docker_image_building}'" >&2 + show_helptext_and_exit fi architecture_to_build="${2:-"${DEFAULT_ARCHITECTURE_TO_BUILD}"}" if [ "${architecture_to_build}" != "amd64" ] && [ "${architecture_to_build}" != "arm64" ]; then echo "Error: Invalid architecture-to-build arg '${architecture_to_build}'" >&2 + show_helptext_and_exit +fi + +debug_image="${3:-"${DEFAULT_DEBUG_IMAGE}"}" +if [ "${debug_image}" != "true" ] && [ "${debug_image}" != "false" ]; then + echo "Error: Invalid debug_image arg: '${debug_image}'" >&2 + show_helptext_and_exit fi +# ============================================================================= +# Main Code +# ============================================================================= + # Checks if dockerignore file is in the root path if ! [ -f "${engine_root_dirpath}"/.dockerignore ]; then echo "Error: No .dockerignore file found in server root '${engine_root_dirpath}'; this is required so Docker caching is enabled and the image builds remain quick" >&2 @@ -61,12 +81,45 @@ echo "Tests succeeded" # Build binary for packaging inside an Alpine Linux image echo "Building server main.go '${MAIN_GO_FILEPATH}'..." -if ! CGO_ENABLED=0 GOOS=linux GOARCH=${architecture_to_build} go build -o "${MAIN_BINARY_OUTPUT_FILEPATH}.${architecture_to_build}" "${MAIN_GO_FILEPATH}"; then +gc_flags="" +if "${debug_image}"; then + gc_flags="all=-N -l" +fi +if ! CGO_ENABLED=0 GOOS=linux GOARCH=${architecture_to_build} go build -gcflags="${gc_flags}" -o "${MAIN_BINARY_OUTPUT_FILEPATH}.${architecture_to_build}" "${MAIN_GO_FILEPATH}"; then echo "Error: An error occurred building the server code" >&2 exit 1 fi echo "Successfully built server code" +# Install and copy delve (the server debugger: https://github.com/go-delve/delve) to include it into the build folder so it can be picked up from the Docker image file +if "${debug_image}"; then + # Checks if the binary already exist + delve_exist="false" + dlv_go_folder_bin_filepath=~/go/bin/linux_"${architecture_to_build}"/"${DELVE_BINARY_FILENAME}" + dlv_bin_in_project_filepath="${DELVE_BINARY_OUTPUT_FILEPATH}.${architecture_to_build}" + # Check if it is in the build folder and skip if it is there + if ! [ -f "$dlv_bin_in_project_filepath" ]; then + # Check if it already exist in the goland folder + if ! [ -f "$dlv_go_folder_bin_filepath" ]; then + echo "Installing delve..." + if ! GOOS=linux GOARCH=${architecture_to_build} go install "${DELVE_SERVER_REPOSITORY_AND_VERSION}"; then + echo "Error: An error occurred installing the delve binary" >&2 + exit 1 + fi + echo "...delve successfully installed." + fi + # Now checks if the binary has been created + if ! [ -f "$dlv_go_folder_bin_filepath" ]; then + echo "Error: expected to have dlv binary in ${dlv_go_folder_bin_filepath} but it is not there." + exit 1 + fi + if ! cp "$dlv_go_folder_bin_filepath" "$dlv_bin_in_project_filepath" ; then + echo "Error: something failed while copying the delve binary from the GO bin folder to the project's build folder" + exit 1 + fi + fi +fi + # Generate Docker image tag if ! cd "${git_repo_dirpath}"; then echo "Error: Couldn't cd to the git root dirpath '${git_repo_dirpath}'" >&2 @@ -83,8 +136,19 @@ if "${skip_docker_image_building}"; then exit 0 fi -dockerfile_filepath="${engine_root_dirpath}/Dockerfile" +dockerfile_filepath="${engine_root_dirpath}/${DOCKER_IMAGE_FILENAME}" + +# Using the Docker debug image if it was requested +if "${debug_image}"; then + dockerfile_filepath="${engine_root_dirpath}/${DOCKER_DEBUG_IMAGE_FILENAME}" +fi + image_name="${IMAGE_ORG_AND_REPO}:${docker_tag}" +# specifying that this is a image for debugging +if "${debug_image}"; then + image_name="${image_name}-${DOCKER_DEBUG_IMAGE_NAME_SUFFIX}" +fi + load_not_push_image=false docker_build_script_cmd="${git_repo_dirpath}/scripts/docker-image-builder.sh ${load_not_push_image} ${dockerfile_filepath} ${image_name}" if ! eval "${docker_build_script_cmd}"; then diff --git a/readme-static-files/goland-engine-breakpoint.png b/readme-static-files/goland-engine-breakpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..afba3fef248669647a6825c26e911a38d427408b GIT binary patch literal 55923 zcmce;cT`ht`!#5xDIh2+o!Ds71VK6xQ4p2hJBak&dmMI&8j#+k_YwgmbfouY z2))-pLNX`#_`L7$n>B0Zo3&>0C!CyfpSxVw-uv2D-l-}pP+w%ac;?I*YQ?9I)z6$E zft)#W_B90=@Rx~enImV;ygsA&SXR^9cx~!XgH;c~?}>+>{aDr}dnfRW;ylaaPj6^% z1if+XxpI+$!+J+@w{f#e{4mLUgfFfWG5XEGv&InZdc-z?b~T!?fOeOOP1SjM z4dnM(hEw}OFjw7+dK%;o%SQBBuff0YF30Qj50Bk$&t3i$A9H)%sKM5-quJS5wxqn= z>q1uetBFd7&jO8@ptq-=rLuCLT}!0cSFSf46#lda85XY;Ew#TL&%AGDjpza;0vREM^y9q356T1cPp!e!jkK zoei6O%$}Jc`6#;L?cc8j^f2H)Fb^SneZS9!{TYpn6&0k7`rq$)yl$eYG24=ReEKQB zbPsb=lnV0a(znBB$-wiWe{a?&0o-H~xZuwt^~s6%CIkQZ=Ht~f!64U`zfZAdH3i;? z0Z04i1q;vrdFb)HP?>8+YR(T9{``Vi%$vtZcX)n%&`WOaBKHW+)BEl;|9Qwv&j$BL z@~}J>)XDqs@VN7w{OvgGj0N(Wpc}5Mz0;MjA0;F#vHoSz?bBx+t`Sf}%;L<+O>z|z zmuR}vhx!0-ATN_MJKtP zBJjR=*3c1X`Y^|5SBQ?q0PafECVai^BjuZS(-?1uVYq?xs@W8Lw+$tK`>VH$by{})bTo!L}OE$0c<={Pr0f;z*z)2=^*W721 z5}}DJc9#$vQE_yXDC5JQ?Qqlu*X-NMS00cI$6P0bbX6eRXAW9BO%6HQ&OH~ETEX1CL%`=d)<_vUUV?i0)ZtD%KZ{2!BO1z$@GW(Y zA_V9`@e@~}-&*mPajBI(yL2LyyCNOXLklm}Escb@JA@5L84iTF)J8apJ1e4Ot8Pjl zR1e%C9Gk;;W*V1W?GYo#)WAf{_k`2HdmkV8`WOnF4iy@hvh6>*dpx6!ExC$ zWb@10wmyEI`8X()s=el;x|Ssgn8FgDIFh`BAoLdvn^Qv|a68Sk-sJdaJ}#y5Lzg6K z>^|#3e7)XD$<#i}D z96b7-F#^LF8-e#~EvCZzJ9Ic*yED==m+6IZ>peg9Z?ta3hw5rk{%BNsF1xi0hD@1X zLP!|pH-pSaRkq9v&%VL?N0{Blh>x0N;PJgPn;#Mf5`%-_a_4`R=8KQ8;0557o#07o z-rlXPtyuP8c;>K^*nR9nW#dNFmywNwT_+27f(z1HU}p1uT#LbZB(|x!Cee

h_l- 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 z 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 + show_helptext_and_exit +fi # ================================================================================================== # Main Logic # ================================================================================================== -for build_script_rel_filepath in "${BUILD_SCRIPT_RELATIVE_FILEPATHS[@]}"; do +# run mandatory scripts first +for build_script_rel_filepath in "${MANDATORY_BUILD_SCRIPT_RELATIVE_FILEPATHS[@]}"; do build_script_abs_filepath="${root_dirpath}/${build_script_rel_filepath}" if ! bash "${build_script_abs_filepath}"; then echo "Error: Build script '${build_script_abs_filepath}' failed" >&2 exit 1 fi done + +# then run the remaining scripts +build_script_rel_filepaths=("${BUILD_SCRIPT_RELATIVE_FILEPATHS[@]}") +if "${debug_image}"; then + build_script_rel_filepaths=("${BUILD_DEBUG_SCRIPT_RELATIVE_FILEPATHS[@]}") +fi + +for build_script_rel_filepath in "${build_script_rel_filepaths[@]}"; do + build_script_abs_filepath="${root_dirpath}/${build_script_rel_filepath}" + if ! bash "${build_script_abs_filepath}" "${debug_image}" ; then + echo "Error: Build script '${build_script_abs_filepath}' failed" >&2 + exit 1 + fi +done diff --git a/scripts/port-forward-engine-debug.sh b/scripts/port-forward-engine-debug.sh new file mode 100755 index 0000000000..07ee982de7 --- /dev/null +++ b/scripts/port-forward-engine-debug.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# 2021-07-08 WATERMARK, DO NOT REMOVE - This script was generated from the Kurtosis Bash script template + +set -euo pipefail # Bash "strict mode" + +# ================================================================================================== +# Constants +# ================================================================================================== + +ENGINE_CONTAINER_K8S_LABEL="kurtosistech.com/resource-type=kurtosis-engine" +# DO NOT CHANGE THIS VALUE +ENGINE_DEBUG_SERVER_PORT=50102 + +# ================================================================================================== +# Main Logic +# ================================================================================================== + +get_engine_namespace_name_cmd="kubectl get ns -l="${ENGINE_CONTAINER_K8S_LABEL}" | cut -d ' ' -f 1 | tail -n +2" +get_engine_pod_name_cmd="kubectl get pods -l="${ENGINE_CONTAINER_K8S_LABEL}" -A | cut -d ' ' -f 1 | tail -n +2" + +# execute the port forward to the debug server inside the engine's container +kubectl port-forward -n $(eval "${get_engine_namespace_name_cmd}") $(eval "${get_engine_pod_name_cmd}") ${ENGINE_DEBUG_SERVER_PORT}:${ENGINE_DEBUG_SERVER_PORT} diff --git a/scripts/set_kt_alias.sh b/scripts/set_kt_alias.sh old mode 100644 new mode 100755 From e707212216c8586f2133b67dc8087e91ff3ca16c Mon Sep 17 00:00:00 2001 From: leoporoli Date: Mon, 5 Feb 2024 13:19:48 -0300 Subject: [PATCH 021/102] chore: debug APIC configuration and readme docs (#2112) ## Description: debug APIC configuration and readme docs ## Is this change user facing? NO ## References (if applicable): --------- Co-authored-by: Edgar Gomes Co-authored-by: Kevin Today Co-authored-by: mieubrisse --- .run/APIC-remote-debug.run.xml | 6 + README.md | 59 +- .../engine_service.pb.go | 562 +++++++++--------- .../lib/kurtosis_context/kurtosis_context.go | 102 +++- .../http_rest/api_types/api_types.gen.go | 18 +- .../engine_rest_api/engine_server.gen.go | 55 +- api/openapi/specs/kurtosis_api.yaml | 11 + api/protobuf/engine/engine_service.proto | 4 + api/rust/src/engine_api.rs | 4 + .../connect/engine_service_pb.d.ts | 8 + .../connect/engine_service_pb.js | 1 + .../engine_service_pb.d.ts | 11 + .../engine_service_pb.js | 50 +- .../src/engine/lib/constructor_calls.ts | 4 +- .../lib/kurtosis_context/kurtosis_context.ts | 55 +- .../src/engine/rest_api_bindings/types.d.ts | 7 + cli/cli/commands/enclave/add/add.go | 21 +- cli/cli/commands/import/import.go | 2 +- ...urtosis_backend_api_container_functions.go | 52 +- ...urtosis_backend_api_container_functions.go | 1 + .../metrics_reporting_kurtosis_backend.go | 2 + .../lib/backend_interface/kurtosis_backend.go | 1 + .../mock_kurtosis_backend.go | 33 +- .../api_container_launcher.go | 4 + core/scripts/build.sh | 24 +- core/server/Dockerfile.debug | 18 + core/server/scripts/_constants.env | 8 + core/server/scripts/build.sh | 84 ++- engine/scripts/build.sh | 2 +- .../engine/enclave_manager/enclave_creator.go | 5 + .../engine/enclave_manager/enclave_manager.go | 3 + .../engine/enclave_manager/enclave_pool.go | 8 +- .../server/engine_connect_server_service.go | 1 + .../engine/server/engine_rest_api_handler.go | 2 + internal_testsuites/typescript/yarn.lock | 474 +++++++-------- .../goland-apic-breakpoint.png | Bin 0 -> 44240 bytes .../goland-apic-debug-button.png | Bin 0 -> 12366 bytes scripts/build.sh | 4 +- scripts/port-forward-apic-debug.sh | 43 ++ 39 files changed, 1125 insertions(+), 624 deletions(-) create mode 100644 .run/APIC-remote-debug.run.xml create mode 100644 core/server/Dockerfile.debug create mode 100644 readme-static-files/goland-apic-breakpoint.png create mode 100644 readme-static-files/goland-apic-debug-button.png create mode 100755 scripts/port-forward-apic-debug.sh diff --git a/.run/APIC-remote-debug.run.xml b/.run/APIC-remote-debug.run.xml new file mode 100644 index 0000000000..33a5ae807f --- /dev/null +++ b/.run/APIC-remote-debug.run.xml @@ -0,0 +1,6 @@ + + + + diff --git a/README.md b/README.md index 97264e8b52..ad13bf1161 100644 --- a/README.md +++ b/README.md @@ -428,10 +428,10 @@ ktdev engine start --debug-mode 4. Then choose the "Engine-remote-debug" run configuration in the "run panel" 5. Press the "debug" button -6. Use the debug panel to inspect the variables value and continue with the debug flow - -7. Make a call to the engine's server (you can use the Kurtosis CLI or Postman) in order to reach out the breakpoint in the code -8. You can debug the CLI and the Kurtosis engine's server at the same time by running it with `ktdebug` instead of `ktdev` mentioned in a previous step, remember to run both remote debug configuration in the Goland IDE. +6. Make a call to the engine's server (you can use the Kurtosis CLI or Postman) in order to reach out the breakpoint in the code +7. Use the debug panel to inspect the variables value and continue with the debug flow + +8. You can debug the CLI and the Kurtosis engine's server at the same time by running it with `ktdebug` instead of `ktdev` mentioned in a previous step, remember to run both remote debug configurations in the Goland IDE. ```bash source ./scripts/set_kt_alias.sh ktdebug engine start @@ -453,6 +453,57 @@ scripts/port-forward-engine-debug.sh ktdev gateway ``` +For running Kurtosis APIC with Golang remote debug: +1. Run the main build script with the first argument `debug_mode` as true. This will generate a new Kurtosis APIC container image which will contain the `debug` suffix in the name. +```bash +scripts/build.sh true +``` +2. Add the breakpoint in the line where you want to stop the cursor. + +3. Run the Kurtosis engine in debug more or not depending on if you want to also debug the engine. +```bash +source ./scripts/set_kt_alias.sh +ktdev engine start --debug-mode + +OR + +ktdev engine start # you will have to build the engine in the regular way `engine/scripts/build.sh` if you choose this version +``` +4. Add a new enclave in debug mode with the `enclave add` command and passing the `debug-mode` flag. This will create a new APIC container with the debug server port bounded and waiting for a connection. +IMPORTANT: You can only run one enclave in debug mode so far, if you want to run another one it will fail due the debug port is already in use, +```bash +ktdev enclave add --debug-mode +``` +5. Then choose the "APIC-remote-debug" run configuration in the "run panel" +6. Press the "debug" button + +7. Find the APIC's GRPC server port in the host machine (you can check it in Docker Desktop or using the Docker CLI, it's the one bounded with the container's 7443 port) +8. Make a call to the APIC's server (you can use the Kurtosis CLI or Postman) in order to reach out the breakpoint in the code +9. Use the debug panel to inspect the variables value and continue with the debug flow + +10. You can debug the CLI, the Kurtosis engine's server and the Kurtosis APIC's server at the same time by running it with `ktdebug` instead of `ktdev` mentioned in a previous step, remember to run the three remote debug configurations in the Goland IDE. +```bash +source ./scripts/set_kt_alias.sh +ktdev engine start --debug-mode +ktdebug enclave add +``` + +Additional steps if you are debugging Kurtosis engine in K8s: + +1. Upload the APIC's image for debug to the K8s cluster +```bash +# for example: +k3d image load kurtosistech/core:5ec6eb-dirty-debug +``` +2. Run the port-forward script before pressing the debug button in Golang (in another terminal instance) to bind the host's port to the container's debug server port +```bash +scripts/port-forward-apic-debug.sh enclave-name +``` +3. Do not forget to run the Kurtosis gateway after calling the APIC's server (in another terminal instance also) +```bash +ktdev gateway +``` + diff --git a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go index 13d42f6d3b..e15990f41e 100644 --- a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go +++ b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go @@ -309,6 +309,9 @@ type CreateEnclaveArgs struct { // The API container log level ApiContainerLogLevel *string `protobuf:"bytes,3,opt,name=api_container_log_level,json=apiContainerLogLevel,proto3,oneof" json:"api_container_log_level,omitempty"` Mode *EnclaveMode `protobuf:"varint,4,opt,name=mode,proto3,enum=engine_api.EnclaveMode,oneof" json:"mode,omitempty"` + // Whether the APIC's container should run with the debug server to receive a remote debug connection + // This is not an EnclaveMode because we will need to debug both current Modes (Test and Prod) + ShouldApicRunInDebugMode *bool `protobuf:"varint,5,opt,name=should_apic_run_in_debug_mode,json=shouldApicRunInDebugMode,proto3,oneof" json:"should_apic_run_in_debug_mode,omitempty"` } func (x *CreateEnclaveArgs) Reset() { @@ -371,6 +374,13 @@ func (x *CreateEnclaveArgs) GetMode() EnclaveMode { return EnclaveMode_TEST } +func (x *CreateEnclaveArgs) GetShouldApicRunInDebugMode() bool { + if x != nil && x.ShouldApicRunInDebugMode != nil { + return *x.ShouldApicRunInDebugMode + } + return false +} + type CreateEnclaveResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1375,7 +1385,7 @@ var file_engine_service_proto_rawDesc = []byte{ 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x22, 0xbd, 0x02, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x22, 0xa5, 0x03, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x26, 0x0a, 0x0c, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3e, @@ -1389,282 +1399,288 @@ var file_engine_service_proto_rawDesc = []byte{ 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x4d, 0x6f, 0x64, - 0x65, 0x48, 0x03, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, - 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x1c, 0x0a, - 0x1a, 0x5f, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x61, 0x67, 0x42, 0x1a, 0x0a, 0x18, 0x5f, - 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, - 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6d, 0x6f, 0x64, 0x65, - 0x22, 0x53, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x65, 0x6e, 0x63, - 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, - 0x6c, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xcd, 0x01, 0x0a, 0x17, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, - 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x70, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x64, - 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x69, 0x70, 0x49, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, - 0x12, 0x37, 0x0a, 0x18, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x6e, - 0x73, 0x69, 0x64, 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x15, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x73, 0x69, - 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x72, 0x69, - 0x64, 0x67, 0x65, 0x5f, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x49, 0x70, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x8b, 0x01, 0x0a, 0x22, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, - 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x48, 0x6f, 0x73, - 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x0a, 0x12, - 0x69, 0x70, 0x5f, 0x6f, 0x6e, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x70, 0x4f, 0x6e, 0x48, 0x6f, - 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x38, 0x0a, 0x19, 0x67, 0x72, 0x70, - 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6d, - 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x67, 0x72, - 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, - 0x69, 0x6e, 0x65, 0x22, 0xcd, 0x04, 0x0a, 0x0b, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x75, - 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x6e, 0x63, 0x6c, 0x61, - 0x76, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x68, - 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x55, 0x75, 0x69, - 0x64, 0x12, 0x50, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x5f, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x65, - 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, - 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x57, 0x0a, 0x14, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, - 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x12, 0x61, 0x70, 0x69, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x51, 0x0a, 0x12, - 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x61, - 0x70, 0x69, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x74, 0x0a, 0x1f, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x63, - 0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x1b, 0x61, 0x70, 0x69, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, - 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, - 0x6f, 0x64, 0x65, 0x22, 0xc3, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, 0x61, - 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0c, 0x65, - 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0b, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x1a, 0x57, 0x0a, 0x10, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, - 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x72, 0x0a, 0x12, 0x45, 0x6e, 0x63, - 0x6c, 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, - 0x21, 0x0a, 0x0c, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x55, 0x75, - 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, - 0x6e, 0x65, 0x64, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x7c, 0x0a, - 0x32, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, - 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0e, 0x61, 0x6c, 0x6c, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x22, 0x40, 0x0a, 0x0f, 0x53, - 0x74, 0x6f, 0x70, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, - 0x0a, 0x12, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x63, 0x6c, - 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x43, 0x0a, - 0x12, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, - 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x11, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x22, 0x4f, 0x0a, 0x09, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x41, 0x72, 0x67, 0x73, 0x12, - 0x2d, 0x0a, 0x10, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x5f, - 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x68, 0x6f, - 0x75, 0x6c, 0x64, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x41, 0x6c, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x13, - 0x0a, 0x11, 0x5f, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x5f, - 0x61, 0x6c, 0x6c, 0x22, 0x3c, 0x0a, 0x12, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, - 0x64, 0x22, 0x73, 0x0a, 0x0d, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x62, 0x0a, 0x1e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x65, 0x6e, - 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, - 0x75, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x64, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, - 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x22, 0xe2, 0x03, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, - 0x12, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x63, 0x6c, 0x61, - 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x10, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x74, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, - 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, - 0x67, 0x73, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, - 0x69, 0x64, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x53, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0b, 0x66, 0x6f, - 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, - 0x00, 0x52, 0x0a, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x6f, 0x67, 0x73, 0x88, 0x01, 0x01, - 0x12, 0x4a, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x6a, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, - 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x69, - 0x6e, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x6a, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x0f, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x0d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x41, - 0x6c, 0x6c, 0x4c, 0x6f, 0x67, 0x73, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x6e, 0x75, 0x6d, - 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, - 0x48, 0x02, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x88, - 0x01, 0x01, 0x1a, 0x41, 0x0a, 0x13, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, - 0x64, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x6e, 0x75, - 0x6d, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0xc4, 0x03, 0x0a, 0x16, - 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x1c, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x42, 0x79, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x18, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x42, 0x79, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x7a, 0x0a, 0x1a, 0x6e, 0x6f, 0x74, - 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, - 0x75, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x55, 0x75, 0x69, 0x64, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x16, 0x6e, - 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, - 0x69, 0x64, 0x53, 0x65, 0x74, 0x1a, 0x60, 0x0a, 0x1d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4c, 0x6f, 0x67, 0x73, 0x42, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, - 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x49, 0x0a, 0x1b, 0x4e, 0x6f, 0x74, 0x46, 0x6f, - 0x75, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x53, 0x65, - 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x57, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, - 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, + 0x65, 0x48, 0x03, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x44, 0x0a, 0x1d, + 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x72, 0x75, 0x6e, 0x5f, + 0x69, 0x6e, 0x5f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x48, 0x04, 0x52, 0x18, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x69, + 0x63, 0x52, 0x75, 0x6e, 0x49, 0x6e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x88, + 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x42, 0x1c, 0x0a, 0x1a, 0x5f, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x61, + 0x67, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x07, 0x0a, + 0x05, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x42, 0x20, 0x0a, 0x1e, 0x5f, 0x73, 0x68, 0x6f, 0x75, 0x6c, + 0x64, 0x5f, 0x61, 0x70, 0x69, 0x63, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x6e, 0x5f, 0x64, 0x65, + 0x62, 0x75, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x53, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x66, + 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, + 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0b, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xcd, 0x01, + 0x0a, 0x17, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, + 0x69, 0x70, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x70, 0x49, 0x6e, 0x73, 0x69, 0x64, + 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x67, 0x72, 0x70, 0x63, + 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x65, 0x6e, 0x63, + 0x6c, 0x61, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x67, 0x72, 0x70, 0x63, + 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, + 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x69, 0x70, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x72, + 0x69, 0x64, 0x67, 0x65, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x8b, 0x01, + 0x0a, 0x22, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x0a, 0x12, 0x69, 0x70, 0x5f, 0x6f, 0x6e, 0x5f, 0x68, 0x6f, + 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x69, 0x70, 0x4f, 0x6e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x12, 0x38, 0x0a, 0x19, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6f, + 0x6e, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, + 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0xcd, 0x04, 0x0a, 0x0b, + 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x65, + 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x5f, + 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x65, 0x6e, 0x65, 0x64, 0x55, 0x75, 0x69, 0x64, 0x12, 0x50, 0x0a, 0x11, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, + 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x57, 0x0a, 0x14, 0x61, + 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, + 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x12, 0x61, 0x70, 0x69, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x51, 0x0a, 0x12, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, + 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x61, 0x70, 0x69, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x1f, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6d, 0x61, + 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, + 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x1b, 0x61, 0x70, 0x69, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x48, 0x6f, + 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, + 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x6b, 0x0a, 0x0d, 0x4c, - 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x08, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, - 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, - 0x69, 0x6e, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, - 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x78, - 0x74, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x2a, 0x27, 0x0a, 0x0b, 0x45, 0x6e, 0x63, 0x6c, - 0x61, 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x53, 0x54, 0x10, - 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, - 0x01, 0x2a, 0x86, 0x01, 0x0a, 0x17, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, - 0x1d, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x00, - 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x52, 0x55, 0x4e, 0x4e, - 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, + 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2b, + 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, + 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0xc3, 0x01, 0x0a, 0x13, + 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0c, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x65, 0x6e, 0x63, + 0x6c, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x57, 0x0a, 0x10, 0x45, 0x6e, 0x63, 0x6c, + 0x61, 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x72, 0x0a, 0x12, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, + 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x5f, 0x75, 0x75, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, + 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x7c, 0x0a, 0x32, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, + 0x6c, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x61, + 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, + 0x2e, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x73, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x73, 0x22, 0x40, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x70, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, + 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x43, 0x0a, 0x12, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, + 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x65, + 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x4f, 0x0a, 0x09, 0x43, 0x6c, + 0x65, 0x61, 0x6e, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x10, 0x73, 0x68, 0x6f, 0x75, 0x6c, + 0x64, 0x5f, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x5f, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x43, 0x6c, 0x65, 0x61, 0x6e, + 0x41, 0x6c, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x68, 0x6f, 0x75, 0x6c, + 0x64, 0x5f, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x5f, 0x61, 0x6c, 0x6c, 0x22, 0x3c, 0x0a, 0x12, 0x45, + 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, + 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x73, 0x0a, 0x0d, 0x43, 0x6c, 0x65, + 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x1e, 0x72, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, + 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, 0x75, + 0x69, 0x64, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x22, 0xe2, + 0x03, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, + 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, + 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x11, 0x65, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, + 0x75, 0x75, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, + 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x53, + 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0b, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x6f, 0x67, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, + 0x77, 0x4c, 0x6f, 0x67, 0x73, 0x88, 0x01, 0x01, 0x12, 0x4a, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x6a, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, + 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x6a, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x61, + 0x6c, 0x6c, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, + 0x0d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x41, 0x6c, 0x6c, 0x4c, 0x6f, 0x67, 0x73, 0x88, 0x01, + 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x6e, 0x75, 0x6d, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x69, 0x6e, + 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x4c, + 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x41, 0x0a, 0x13, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0e, 0x0a, + 0x0c, 0x5f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x42, 0x12, 0x0a, + 0x10, 0x5f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6c, 0x6f, 0x67, + 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x69, + 0x6e, 0x65, 0x73, 0x22, 0xc4, 0x03, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, + 0x01, 0x0a, 0x1c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x5f, + 0x62, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, + 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x42, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, + 0x69, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4c, 0x6f, 0x67, 0x73, 0x42, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, + 0x64, 0x12, 0x7a, 0x0a, 0x1a, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, + 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, + 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x53, 0x65, 0x74, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x16, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x53, 0x65, 0x74, 0x1a, 0x60, 0x0a, + 0x1d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x42, 0x79, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, + 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x49, 0x0a, 0x1b, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x57, 0x0a, 0x07, 0x4c, 0x6f, + 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x22, 0x6b, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x21, 0x0a, + 0x0c, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x78, 0x74, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, + 0x2a, 0x27, 0x0a, 0x0b, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, + 0x08, 0x0a, 0x04, 0x54, 0x45, 0x53, 0x54, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x4f, + 0x44, 0x55, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x2a, 0x86, 0x01, 0x0a, 0x17, 0x45, 0x6e, + 0x63, 0x6c, 0x61, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x1d, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x19, 0x45, - 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x25, 0x45, 0x6e, 0x63, 0x6c, - 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x58, 0x49, 0x53, 0x54, 0x45, 0x4e, - 0x54, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, + 0x5f, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x00, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x6e, 0x63, 0x6c, + 0x61, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x23, 0x0a, + 0x1f, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, + 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x19, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x25, 0x0a, 0x21, 0x45, 0x6e, - 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, - 0x02, 0x2a, 0xc3, 0x01, 0x0a, 0x0f, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x4f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x21, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x43, 0x4f, - 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x10, 0x00, 0x12, 0x29, 0x0a, 0x25, - 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, - 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, - 0x5f, 0x54, 0x45, 0x58, 0x54, 0x10, 0x01, 0x12, 0x2c, 0x0a, 0x28, 0x4c, 0x6f, 0x67, 0x4c, 0x69, - 0x6e, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, - 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x52, 0x45, - 0x47, 0x45, 0x58, 0x10, 0x02, 0x12, 0x30, 0x0a, 0x2c, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x4e, 0x4f, - 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, - 0x52, 0x45, 0x47, 0x45, 0x58, 0x10, 0x03, 0x32, 0xae, 0x05, 0x0a, 0x0d, 0x45, 0x6e, 0x67, 0x69, - 0x6e, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x47, 0x65, 0x74, - 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x1d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, - 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x21, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, - 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, - 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x86, 0x01, 0x0a, 0x2a, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, - 0x63, 0x61, 0x6c, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x3e, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, - 0x63, 0x61, 0x6c, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x44, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x1b, - 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x70, - 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, - 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x1e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x63, 0x6c, - 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, - 0x00, 0x12, 0x3b, 0x0a, 0x05, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x12, 0x15, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x41, 0x72, 0x67, - 0x73, 0x1a, 0x19, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, - 0x6c, 0x65, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, - 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, - 0x12, 0x1e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x41, 0x72, 0x67, 0x73, - 0x1a, 0x22, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0x56, 0x5a, 0x54, 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, 0x61, 0x70, - 0x69, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, - 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, - 0x72, 0x70, 0x63, 0x5f, 0x61, 0x70, 0x69, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x29, 0x0a, 0x25, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x4e, 0x4f, + 0x4e, 0x45, 0x58, 0x49, 0x53, 0x54, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x45, + 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, + 0x10, 0x01, 0x12, 0x25, 0x0a, 0x21, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x50, 0x49, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, + 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xc3, 0x01, 0x0a, 0x0f, 0x4c, 0x6f, + 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x25, 0x0a, + 0x21, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x45, + 0x58, 0x54, 0x10, 0x00, 0x12, 0x29, 0x0a, 0x25, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x4e, 0x4f, 0x54, + 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x10, 0x01, 0x12, + 0x2c, 0x0a, 0x28, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x5f, + 0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x52, 0x45, 0x47, 0x45, 0x58, 0x10, 0x02, 0x12, 0x30, 0x0a, + 0x2c, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x5f, 0x44, 0x4f, 0x45, 0x53, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, + 0x4e, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x52, 0x45, 0x47, 0x45, 0x58, 0x10, 0x03, 0x32, + 0xae, 0x05, 0x0a, 0x0d, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x53, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, + 0x12, 0x1d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, + 0x21, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x65, 0x6e, + 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x6c, + 0x61, 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x86, + 0x01, 0x0a, 0x2a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, + 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x3e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, + 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, + 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x45, 0x6e, 0x63, 0x6c, 0x61, + 0x76, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x45, + 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, 0x1b, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, + 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x4a, 0x0a, + 0x0e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x12, + 0x1e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x73, + 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x63, 0x6c, 0x61, 0x76, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x05, 0x43, 0x6c, 0x65, + 0x61, 0x6e, 0x12, 0x15, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, + 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x19, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1e, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4c, 0x6f, 0x67, 0x73, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x22, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, + 0x42, 0x56, 0x5a, 0x54, 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, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, + 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, + 0x5f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x72, 0x70, 0x63, 0x5f, 0x61, 0x70, 0x69, 0x5f, + 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/golang/engine/lib/kurtosis_context/kurtosis_context.go b/api/golang/engine/lib/kurtosis_context/kurtosis_context.go index 7c25447eba..ed94156386 100644 --- a/api/golang/engine/lib/kurtosis_context/kurtosis_context.go +++ b/api/golang/engine/lib/kurtosis_context/kurtosis_context.go @@ -39,6 +39,9 @@ const ( validUuidMatchesAllowed = 1 portalIsRequired = true + + defaultShouldAPICRunInDebugMode = false + runAPICInDebugMode = true ) var ( @@ -105,10 +108,7 @@ func NewKurtosisContextFromLocalEngine() (*KurtosisContext, error) { } // Docs available at https://docs.kurtosis.com/sdk#createenclaveenclaveid-enclaveid-boolean-issubnetworkingenabled---enclavecontextenclavecontext-enclavecontext -func (kurtosisCtx *KurtosisContext) CreateEnclave( - ctx context.Context, - enclaveName string, -) (*enclaves.EnclaveContext, error) { +func (kurtosisCtx *KurtosisContext) CreateEnclave(ctx context.Context, enclaveName string) (*enclaves.EnclaveContext, error) { createEnclaveArgs := newCreateEnclaveArgsWithDefaultValues(enclaveName) @@ -125,12 +125,29 @@ func (kurtosisCtx *KurtosisContext) CreateEnclave( return enclaveContext, nil } -// Docs available at https://docs.kurtosis.com/sdk#createenclaveenclaveid-enclaveid-boolean-issubnetworkingenabled---enclavecontextenclavecontext-enclavecontext -func (kurtosisCtx *KurtosisContext) CreateProductionEnclave( +func (kurtosisCtx *KurtosisContext) CreateEnclaveWithDebugEnabled( ctx context.Context, enclaveName string, ) (*enclaves.EnclaveContext, error) { + createEnclaveArgs := newCreateEnclaveArgsWithDefaultValuesForDebugging(enclaveName) + + response, err := kurtosisCtx.engineClient.CreateEnclave(ctx, createEnclaveArgs) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred creating an enclave with name '%v'", enclaveName) + } + + enclaveContext, err := newEnclaveContextFromEnclaveInfo(ctx, kurtosisCtx.portalClient, response.EnclaveInfo) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred creating an enclave context from a newly-created enclave; this should never happen") + } + + return enclaveContext, nil +} + +// Docs available at https://docs.kurtosis.com/sdk#createenclaveenclaveid-enclaveid-boolean-issubnetworkingenabled---enclavecontextenclavecontext-enclavecontext +func (kurtosisCtx *KurtosisContext) CreateProductionEnclave(ctx context.Context, enclaveName string) (*enclaves.EnclaveContext, error) { + createEnclaveArgs := newCreateProductionEnclaveWithDefaultValues(enclaveName) response, err := kurtosisCtx.engineClient.CreateEnclave(ctx, createEnclaveArgs) @@ -146,6 +163,23 @@ func (kurtosisCtx *KurtosisContext) CreateProductionEnclave( return enclaveContext, nil } +func (kurtosisCtx *KurtosisContext) CreateProductionEnclaveWithDebugEnabled(ctx context.Context, enclaveName string) (*enclaves.EnclaveContext, error) { + + createEnclaveArgs := newCreateProductionEnclaveWithDefaultValuesForDebugging(enclaveName) + + response, err := kurtosisCtx.engineClient.CreateEnclave(ctx, createEnclaveArgs) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred creating an enclave with name '%v'", enclaveName) + } + + enclaveContext, err := newEnclaveContextFromEnclaveInfo(ctx, kurtosisCtx.portalClient, response.EnclaveInfo) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred creating an enclave context from a newly-created enclave; this should never happen") + } + + return enclaveContext, nil +} + // Docs available at https://docs.kurtosis.com/sdk/#getenclavecontextstring-enclaveidentifier---enclavecontextenclavecontext-enclavecontext func (kurtosisCtx *KurtosisContext) GetEnclaveContext(ctx context.Context, enclaveIdentifier string) (*enclaves.EnclaveContext, error) { enclaveInfo, err := kurtosisCtx.GetEnclave(ctx, enclaveIdentifier) @@ -594,12 +628,32 @@ func newCreateEnclaveArgsWithDefaultValues(enclaveName string) *kurtosis_engine_ defaultApiContainerVersionTag := defaultApiContainerVersionTagStr defaultApiContainerLogLevel := defaultApiContainerLogLevelStr defaultEnclaveMode := kurtosis_engine_rpc_api_bindings.EnclaveMode_TEST + shouldApicRunInDebugMode := defaultShouldAPICRunInDebugMode createEnclaveArgs := &kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs{ - EnclaveName: &enclaveName, - ApiContainerVersionTag: &defaultApiContainerVersionTag, - ApiContainerLogLevel: &defaultApiContainerLogLevel, - Mode: &defaultEnclaveMode, + EnclaveName: &enclaveName, + ApiContainerVersionTag: &defaultApiContainerVersionTag, + ApiContainerLogLevel: &defaultApiContainerLogLevel, + Mode: &defaultEnclaveMode, + ShouldApicRunInDebugMode: &shouldApicRunInDebugMode, + } + + return createEnclaveArgs +} + +func newCreateEnclaveArgsWithDefaultValuesForDebugging(enclaveName string) *kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs { + + defaultApiContainerVersionTag := defaultApiContainerVersionTagStr + defaultApiContainerLogLevel := defaultApiContainerLogLevelStr + defaultEnclaveMode := kurtosis_engine_rpc_api_bindings.EnclaveMode_TEST + shouldApicRunInDebugMode := runAPICInDebugMode + + createEnclaveArgs := &kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs{ + EnclaveName: &enclaveName, + ApiContainerVersionTag: &defaultApiContainerVersionTag, + ApiContainerLogLevel: &defaultApiContainerLogLevel, + Mode: &defaultEnclaveMode, + ShouldApicRunInDebugMode: &shouldApicRunInDebugMode, } return createEnclaveArgs @@ -610,12 +664,32 @@ func newCreateProductionEnclaveWithDefaultValues(enclaveName string) *kurtosis_e defaultApiContainerVersionTag := defaultApiContainerVersionTagStr defaultApiContainerLogLevel := defaultApiContainerLogLevelStr defaultEnclaveMode := kurtosis_engine_rpc_api_bindings.EnclaveMode_PRODUCTION + shouldApicRunInDebugMode := defaultShouldAPICRunInDebugMode + + createEnclaveArgs := &kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs{ + EnclaveName: &enclaveName, + ApiContainerVersionTag: &defaultApiContainerVersionTag, + ApiContainerLogLevel: &defaultApiContainerLogLevel, + Mode: &defaultEnclaveMode, + ShouldApicRunInDebugMode: &shouldApicRunInDebugMode, + } + + return createEnclaveArgs +} + +func newCreateProductionEnclaveWithDefaultValuesForDebugging(enclaveName string) *kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs { + + defaultApiContainerVersionTag := defaultApiContainerVersionTagStr + defaultApiContainerLogLevel := defaultApiContainerLogLevelStr + defaultEnclaveMode := kurtosis_engine_rpc_api_bindings.EnclaveMode_PRODUCTION + shouldApicRunInDebugMode := runAPICInDebugMode createEnclaveArgs := &kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs{ - EnclaveName: &enclaveName, - ApiContainerVersionTag: &defaultApiContainerVersionTag, - ApiContainerLogLevel: &defaultApiContainerLogLevel, - Mode: &defaultEnclaveMode, + EnclaveName: &enclaveName, + ApiContainerVersionTag: &defaultApiContainerVersionTag, + ApiContainerLogLevel: &defaultApiContainerLogLevel, + Mode: &defaultEnclaveMode, + ShouldApicRunInDebugMode: &shouldApicRunInDebugMode, } return createEnclaveArgs diff --git a/api/golang/http_rest/api_types/api_types.gen.go b/api/golang/http_rest/api_types/api_types.gen.go index cf318293f4..877ee1b211 100644 --- a/api/golang/http_rest/api_types/api_types.gen.go +++ b/api/golang/http_rest/api_types/api_types.gen.go @@ -11,6 +11,12 @@ import ( openapi_types "github.com/oapi-codegen/runtime/types" ) +// Defines values for ApiContainerDebugMode. +const ( + False ApiContainerDebugMode = false + True ApiContainerDebugMode = true +) + // Defines values for ApiContainerStatus. const ( ApiContainerStatusNONEXISTENT ApiContainerStatus = "NON_EXISTENT" @@ -101,6 +107,9 @@ const ( UDP TransportProtocol = "UDP" ) +// ApiContainerDebugMode defines model for ApiContainerDebugMode. +type ApiContainerDebugMode bool + // ApiContainerStatus defines model for ApiContainerStatus. type ApiContainerStatus string @@ -137,10 +146,11 @@ type ContainerStatus string // CreateEnclave defines model for CreateEnclave. type CreateEnclave struct { // ApiContainerLogLevel Enclave log level, defaults to INFO - ApiContainerLogLevel *string `json:"api_container_log_level,omitempty"` - ApiContainerVersionTag string `json:"api_container_version_tag"` - EnclaveName string `json:"enclave_name"` - Mode *EnclaveMode `json:"mode,omitempty"` + ApiContainerLogLevel *string `json:"api_container_log_level,omitempty"` + ApiContainerVersionTag string `json:"api_container_version_tag"` + EnclaveName string `json:"enclave_name"` + Mode *EnclaveMode `json:"mode,omitempty"` + ShouldApicRunInDebugMode *ApiContainerDebugMode `json:"should_apic_run_in_debug_mode,omitempty"` } // DeletionSummary defines model for DeletionSummary. diff --git a/api/golang/http_rest/server/engine_rest_api/engine_server.gen.go b/api/golang/http_rest/server/engine_rest_api/engine_server.gen.go index b0a2ed1e40..032f122253 100644 --- a/api/golang/http_rest/server/engine_rest_api/engine_server.gen.go +++ b/api/golang/http_rest/server/engine_rest_api/engine_server.gen.go @@ -750,33 +750,34 @@ func (sh *strictHandler) GetEngineInfo(ctx echo.Context) error { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "iCcuE2ssTUsvan866rf7/wMAAP//IQrWlZYgAAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/openapi/specs/kurtosis_api.yaml b/api/openapi/specs/kurtosis_api.yaml index c5627cf706..150cc927bd 100644 --- a/api/openapi/specs/kurtosis_api.yaml +++ b/api/openapi/specs/kurtosis_api.yaml @@ -840,6 +840,10 @@ components: mode: $ref: "#/components/schemas/EnclaveMode" description: Enclave mode, defaults to TEST + should_apic_run_in_debug_mode: + $ref: "#/components/schemas/ApiContainerDebugMode" + # This is not an EnclaveMode because we will need to debug both current Modes (Test and Prod) + description: Whether the APIC's container should run with the debug server to receive a remote debug connection required: - enclave_name - api_container_version_tag @@ -869,6 +873,13 @@ components: - STOPPED - NON_EXISTENT + ApiContainerDebugMode: + type: boolean + default: false + enum: + - false + - true + EnclaveInfo: type: object properties: diff --git a/api/protobuf/engine/engine_service.proto b/api/protobuf/engine/engine_service.proto index 87e40bcdbb..0e8dcfb022 100644 --- a/api/protobuf/engine/engine_service.proto +++ b/api/protobuf/engine/engine_service.proto @@ -51,6 +51,10 @@ message CreateEnclaveArgs { optional string api_container_log_level = 3; optional EnclaveMode mode = 4; + + // Whether the APIC's container should run with the debug server to receive a remote debug connection + // This is not an EnclaveMode because we will need to debug both current Modes (Test and Prod) + optional bool should_apic_run_in_debug_mode = 5; } enum EnclaveMode { diff --git a/api/rust/src/engine_api.rs b/api/rust/src/engine_api.rs index c816267b66..c2d281cc69 100644 --- a/api/rust/src/engine_api.rs +++ b/api/rust/src/engine_api.rs @@ -28,6 +28,10 @@ pub struct CreateEnclaveArgs { pub api_container_log_level: ::core::option::Option<::prost::alloc::string::String>, #[prost(enumeration = "EnclaveMode", optional, tag = "4")] pub mode: ::core::option::Option, + /// Whether the APIC's container should run with the debug server to receive a remote debug connection + /// This is not an EnclaveMode because we will need to debug both current Modes (Test and Prod) + #[prost(bool, optional, tag = "5")] + pub should_apic_run_in_debug_mode: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts index f0ab878b01..269a10fde9 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts @@ -177,6 +177,14 @@ export declare class CreateEnclaveArgs extends Message { */ mode?: EnclaveMode; + /** + * Whether the APIC's container should run with the debug server to receive a remote debug connection + * This is not an EnclaveMode because we will need to debug both current Modes (Test and Prod) + * + * @generated from field: optional bool should_apic_run_in_debug_mode = 5; + */ + shouldApicRunInDebugMode?: boolean; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js index 6a8a9cfd9c..24c994a6f4 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js @@ -92,6 +92,7 @@ export const CreateEnclaveArgs = proto3.makeMessageType( { no: 2, name: "api_container_version_tag", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 3, name: "api_container_log_level", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 4, name: "mode", kind: "enum", T: proto3.getEnumType(EnclaveMode), opt: true }, + { no: 5, name: "should_apic_run_in_debug_mode", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, ], ); diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts index ee7c174bda..06fc96768c 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts @@ -43,6 +43,11 @@ export class CreateEnclaveArgs extends jspb.Message { hasMode(): boolean; clearMode(): CreateEnclaveArgs; + getShouldApicRunInDebugMode(): boolean; + setShouldApicRunInDebugMode(value: boolean): CreateEnclaveArgs; + hasShouldApicRunInDebugMode(): boolean; + clearShouldApicRunInDebugMode(): CreateEnclaveArgs; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): CreateEnclaveArgs.AsObject; static toObject(includeInstance: boolean, msg: CreateEnclaveArgs): CreateEnclaveArgs.AsObject; @@ -57,6 +62,7 @@ export namespace CreateEnclaveArgs { apiContainerVersionTag?: string, apiContainerLogLevel?: string, mode?: EnclaveMode, + shouldApicRunInDebugMode?: boolean, } export enum EnclaveNameCase { @@ -78,6 +84,11 @@ export namespace CreateEnclaveArgs { _MODE_NOT_SET = 0, MODE = 4, } + + export enum ShouldApicRunInDebugModeCase { + _SHOULD_APIC_RUN_IN_DEBUG_MODE_NOT_SET = 0, + SHOULD_APIC_RUN_IN_DEBUG_MODE = 5, + } } export class CreateEnclaveResponse extends jspb.Message { diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.js b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.js index 32f4a327a6..dbd7436b72 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.js +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.js @@ -590,7 +590,8 @@ proto.engine_api.CreateEnclaveArgs.toObject = function(includeInstance, msg) { enclaveName: jspb.Message.getFieldWithDefault(msg, 1, ""), apiContainerVersionTag: jspb.Message.getFieldWithDefault(msg, 2, ""), apiContainerLogLevel: jspb.Message.getFieldWithDefault(msg, 3, ""), - mode: jspb.Message.getFieldWithDefault(msg, 4, 0) + mode: jspb.Message.getFieldWithDefault(msg, 4, 0), + shouldApicRunInDebugMode: jspb.Message.getBooleanFieldWithDefault(msg, 5, false) }; if (includeInstance) { @@ -643,6 +644,10 @@ proto.engine_api.CreateEnclaveArgs.deserializeBinaryFromReader = function(msg, r var value = /** @type {!proto.engine_api.EnclaveMode} */ (reader.readEnum()); msg.setMode(value); break; + case 5: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setShouldApicRunInDebugMode(value); + break; default: reader.skipField(); break; @@ -700,6 +705,13 @@ proto.engine_api.CreateEnclaveArgs.serializeBinaryToWriter = function(message, w f ); } + f = /** @type {boolean} */ (jspb.Message.getField(message, 5)); + if (f != null) { + writer.writeBool( + 5, + f + ); + } }; @@ -847,6 +859,42 @@ proto.engine_api.CreateEnclaveArgs.prototype.hasMode = function() { }; +/** + * optional bool should_apic_run_in_debug_mode = 5; + * @return {boolean} + */ +proto.engine_api.CreateEnclaveArgs.prototype.getShouldApicRunInDebugMode = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.engine_api.CreateEnclaveArgs} returns this + */ +proto.engine_api.CreateEnclaveArgs.prototype.setShouldApicRunInDebugMode = function(value) { + return jspb.Message.setField(this, 5, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.engine_api.CreateEnclaveArgs} returns this + */ +proto.engine_api.CreateEnclaveArgs.prototype.clearShouldApicRunInDebugMode = function() { + return jspb.Message.setField(this, 5, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.engine_api.CreateEnclaveArgs.prototype.hasShouldApicRunInDebugMode = function() { + return jspb.Message.getField(this, 5) != null; +}; + + diff --git a/api/typescript/src/engine/lib/constructor_calls.ts b/api/typescript/src/engine/lib/constructor_calls.ts index ec61fd68dc..30f99a3ab7 100644 --- a/api/typescript/src/engine/lib/constructor_calls.ts +++ b/api/typescript/src/engine/lib/constructor_calls.ts @@ -18,11 +18,13 @@ import * as kurtosisLogLineOperator from "./kurtosis_context/log_line_operator"; export function newCreateEnclaveArgs( enclaveName: string, apiContainerImageVersionTag: string, - apiContainerLogLevel: string): CreateEnclaveArgs { + apiContainerLogLevel: string, + shouldApicRunInDebugMode: boolean): CreateEnclaveArgs { const result: CreateEnclaveArgs = new CreateEnclaveArgs(); result.setEnclaveName(enclaveName); result.setApiContainerVersionTag(apiContainerImageVersionTag); result.setApiContainerLogLevel(apiContainerLogLevel); + result.setShouldApicRunInDebugMode(shouldApicRunInDebugMode); return result; } diff --git a/api/typescript/src/engine/lib/kurtosis_context/kurtosis_context.ts b/api/typescript/src/engine/lib/kurtosis_context/kurtosis_context.ts index 88ee484564..1502dcd845 100644 --- a/api/typescript/src/engine/lib/kurtosis_context/kurtosis_context.ts +++ b/api/typescript/src/engine/lib/kurtosis_context/kurtosis_context.ts @@ -44,6 +44,9 @@ export const DEFAULT_GRPC_ENGINE_SERVER_PORT_NUM: number = 9710; // Blank tells the engine server to use the default const DEFAULT_API_CONTAINER_VERSION_TAG = ""; +const DEFAULT_SHOULD_APIC_RUN_IN_DEBUG_MODE = false +const RUN_APIC_IN_DEBUG_MODE = true + // Docs available at https://docs.kurtosis.com/sdk#kurtosiscontext export class KurtosisContext { private readonly client: GenericEngineClient @@ -88,32 +91,25 @@ export class KurtosisContext { // Docs available at https://docs.kurtosis.com/sdk#createenclaveenclaveid-enclaveid-boolean-issubnetworkingenabled---enclavecontextenclavecontext-enclavecontext public async createEnclave(enclaveName: string): Promise> { - const subnetworkDisableBecauseItIsDeprecated = false const enclaveArgs: CreateEnclaveArgs = newCreateEnclaveArgs( enclaveName, DEFAULT_API_CONTAINER_VERSION_TAG, API_CONTAINER_LOG_LEVEL, + DEFAULT_SHOULD_APIC_RUN_IN_DEBUG_MODE, ); - const getEnclaveResponseResult = await this.client.createEnclaveResponse(enclaveArgs) - if(getEnclaveResponseResult.isErr()){ - return err(getEnclaveResponseResult.error) - } - - const enclaveResponse: CreateEnclaveResponse = getEnclaveResponseResult.value; - const enclaveInfo: EnclaveInfo | undefined = enclaveResponse.getEnclaveInfo(); - if (enclaveInfo === undefined) { - return err(new Error("An error occurred creating enclave with name " + enclaveName + " enclaveInfo is undefined; " + - "this is a bug on this library" )) - } + return this.createEnclaveWithArgs(enclaveArgs) + } - const newEnclaveContextResult: Result = await this.newEnclaveContextFromEnclaveInfo(enclaveInfo); - if (newEnclaveContextResult.isErr()) { - return err(new Error(`An error occurred creating an enclave context from a newly-created enclave; this should never happen`)) - } + public async createEnclaveWithDebugEnabled(enclaveName: string): Promise> { + const enclaveArgs: CreateEnclaveArgs = newCreateEnclaveArgs( + enclaveName, + DEFAULT_API_CONTAINER_VERSION_TAG, + API_CONTAINER_LOG_LEVEL, + RUN_APIC_IN_DEBUG_MODE, + ); - const enclaveContext = newEnclaveContextResult.value - return ok(enclaveContext); + return this.createEnclaveWithArgs(enclaveArgs) } // Docs available at https://docs.kurtosis.com/sdk/#getenclavecontextstring-enclaveidentifier---enclavecontextenclavecontext-enclavecontext @@ -351,4 +347,27 @@ export class KurtosisContext { return ok(null) } + + async createEnclaveWithArgs(enclaveArgs: CreateEnclaveArgs): Promise> { + const getEnclaveResponseResult = await this.client.createEnclaveResponse(enclaveArgs) + if(getEnclaveResponseResult.isErr()){ + return err(getEnclaveResponseResult.error) + } + + const enclaveResponse: CreateEnclaveResponse = getEnclaveResponseResult.value; + const enclaveInfo: EnclaveInfo | undefined = enclaveResponse.getEnclaveInfo(); + if (enclaveInfo === undefined) { + return err(new Error("An error occurred creating enclave with name " + enclaveArgs.getEnclaveName() + " enclaveInfo is undefined; " + + "this is a bug on this library" )) + } + + const newEnclaveContextResult: Result = await this.newEnclaveContextFromEnclaveInfo(enclaveInfo); + if (newEnclaveContextResult.isErr()) { + return err(new Error(`An error occurred creating an enclave context from a newly-created enclave; this should never happen`)) + } + + const enclaveContext = newEnclaveContextResult.value + return ok(enclaveContext); + } + } diff --git a/api/typescript/src/engine/rest_api_bindings/types.d.ts b/api/typescript/src/engine/rest_api_bindings/types.d.ts index d51959c210..4764b775d6 100644 --- a/api/typescript/src/engine/rest_api_bindings/types.d.ts +++ b/api/typescript/src/engine/rest_api_bindings/types.d.ts @@ -1194,6 +1194,8 @@ export interface components { api_container_log_level?: string; /** @description Enclave mode, defaults to TEST */ mode?: components["schemas"]["EnclaveMode"]; + /** @description Whether the APIC's container should run with the debug server to receive a remote debug connection */ + should_apic_run_in_debug_mode?: components["schemas"]["ApiContainerDebugMode"]; }; /** @enum {string} */ EnclaveMode: "TEST" | "PRODUCTION"; @@ -1203,6 +1205,11 @@ export interface components { EnclaveTargetStatus: "STOP"; /** @enum {string} */ ApiContainerStatus: "RUNNING" | "STOPPED" | "NON_EXISTENT"; + /** + * @default false + * @enum {boolean} + */ + ApiContainerDebugMode: false | true; EnclaveInfo: { enclave_uuid: string; name: string; diff --git a/cli/cli/commands/enclave/add/add.go b/cli/cli/commands/enclave/add/add.go index 7e2e8293ff..e9cbb57c0a 100644 --- a/cli/cli/commands/enclave/add/add.go +++ b/cli/cli/commands/enclave/add/add.go @@ -14,6 +14,7 @@ import ( "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/logrus_log_levels" "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/output_printers" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface" + "github.com/kurtosis-tech/kurtosis/kurtosis_version" "github.com/kurtosis-tech/kurtosis/metrics-library/golang/lib/metrics_client" "github.com/kurtosis-tech/stacktrace" "github.com/sirupsen/logrus" @@ -93,6 +94,17 @@ func run( return stacktrace.Propagate(err, "An error occurred while getting the API Container Version using flag with key '%v'; this is a bug in Kurtosis", apiContainerVersionFlagKey) } + isDebugMode, err := flags.GetBool(defaults.DebugModeFlagKey) + if err != nil { + return stacktrace.Propagate(err, "Expected a value for the '%v' flag but failed to get it", defaults.DebugModeFlagKey) + } + + shouldApicRunInDebugMode := defaults.DefaultEnableDebugMode + if isDebugMode && apiContainerVersion == defaults.DefaultAPIContainerVersion { + apiContainerVersion = fmt.Sprintf("%s-%s", kurtosis_version.KurtosisVersion, defaults.DefaultKurtosisContainerDebugImageNameSuffix) + shouldApicRunInDebugMode = true + } + kurtosisLogLevelStr, err := flags.GetString(apiContainerLogLevelFlagKey) if err != nil { return stacktrace.Propagate(err, "An error occurred while getting the API Container log level using flag with key '%v'; this is a bug in Kurtosis", apiContainerLogLevelFlagKey) @@ -131,10 +143,11 @@ func run( } createEnclaveArgs := &kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs{ - EnclaveName: &enclaveName, - ApiContainerVersionTag: &apiContainerVersion, - ApiContainerLogLevel: &kurtosisLogLevelStr, - Mode: &mode, + EnclaveName: &enclaveName, + ApiContainerVersionTag: &apiContainerVersion, + ApiContainerLogLevel: &kurtosisLogLevelStr, + Mode: &mode, + ShouldApicRunInDebugMode: &shouldApicRunInDebugMode, } createdEnclaveResponse, err := engineClient.CreateEnclave(ctx, createEnclaveArgs) if err != nil { diff --git a/cli/cli/commands/import/import.go b/cli/cli/commands/import/import.go index 42add2faff..13583d192f 100644 --- a/cli/cli/commands/import/import.go +++ b/cli/cli/commands/import/import.go @@ -11,7 +11,6 @@ import ( "github.com/kurtosis-tech/kurtosis/api/golang/engine/kurtosis_engine_rpc_api_bindings" enclave_consts "github.com/kurtosis-tech/kurtosis/api/golang/engine/lib/enclave" "github.com/kurtosis-tech/kurtosis/api/golang/engine/lib/kurtosis_context" - command_args_run "github.com/kurtosis-tech/kurtosis/cli/cli/command_args/run" "github.com/kurtosis-tech/kurtosis/cli/cli/command_framework/highlevel/engine_consuming_kurtosis_command" "github.com/kurtosis-tech/kurtosis/cli/cli/command_framework/highlevel/file_system_path_arg" @@ -155,6 +154,7 @@ func run( if err != nil { return stacktrace.Propagate(err, "Couldn't find enclave name flag '%v'", enclaveNameFlagKey) } + enclaveCtx, err := createEnclave(ctx, kurtosisCtx, enclaveName) if err != nil { return stacktrace.Propagate(err, "Couldn't create enclave") diff --git a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend_api_container_functions.go b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend_api_container_functions.go index c39caf00ce..bfbf53d0a2 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend_api_container_functions.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend_api_container_functions.go @@ -31,6 +31,8 @@ const ( maxWaitForApiContainerAvailabilityRetries = 10 timeBetweenWaitForApiContainerAvailabilityRetries = 1 * time.Second + + apicDebugServerPort = 50103 // in ClI this is 50101 and in engine is 50102 ) // TODO: MIGRATE THIS FOLDER TO USE STRUCTURE OF USER_SERVICE_FUNCTIONS MODULE @@ -46,6 +48,7 @@ func (backend *DockerKurtosisBackend) CreateAPIContainer( enclaveDataVolumeDirpath string, ownIpAddressEnvVar string, customEnvVars map[string]string, + shouldStartInDebugMode bool, ) (*api_container.APIContainer, error) { logrus.Debugf("Creating the APIC for enclave '%v'", enclaveUuid) @@ -89,7 +92,7 @@ func (backend *DockerKurtosisBackend) CreateAPIContainer( } reverseProxyEnclaveNetworkIpAddress, found := reverseProxy.GetEnclaveNetworksIpAddress()[enclaveNetwork.GetId()] if !found { - return nil, stacktrace.NewError("An error occured while getting the reverse proxy enclave network IP address for enclave '%v', This is a bug in Kurtosis", enclaveUuid) + return nil, stacktrace.NewError("An error occurred while getting the reverse proxy enclave network IP address for enclave '%v', This is a bug in Kurtosis", enclaveUuid) } networkCidr := enclaveNetwork.GetIpAndMask() @@ -127,7 +130,7 @@ func (backend *DockerKurtosisBackend) CreateAPIContainer( err, "An error occurred creating the API container's private grpc port spec object using number '%v' and protocol '%v'", grpcPortNum, - consts.EngineTransportProtocol.String(), + apiContainerTransportProtocol, ) } @@ -149,10 +152,35 @@ func (backend *DockerKurtosisBackend) CreateAPIContainer( if err != nil { return nil, stacktrace.Propagate(err, "An error occurred transforming the private grpc port spec to a Docker port") } + usedPorts := map[nat.Port]docker_manager.PortPublishSpec{ privateGrpcDockerPort: docker_manager.NewAutomaticPublishingSpec(), } + if shouldStartInDebugMode { + debugServerPortSpec, err := port_spec.NewPortSpec( + uint16(apicDebugServerPort), + apiContainerTransportProtocol, + consts.HttpApplicationProtocol, + defaultWait, + ) + if err != nil { + return nil, stacktrace.Propagate( + err, + "An error occurred creating the API container's debug server port spec object using number '%v' and protocol '%v'", + apicDebugServerPort, + apiContainerTransportProtocol, + ) + } + + debugServerDockerPort, err := shared_helpers.TransformPortSpecToDockerPort(debugServerPortSpec) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred transforming the debug server port spec to a Docker port") + } + + usedPorts[debugServerDockerPort] = docker_manager.NewManualPublishingSpec(uint16(apicDebugServerPort)) + } + bindMounts := map[string]string{ // Necessary so that the API container can interact with the Docker engine consts.DockerSocketFilepath: consts.DockerSocketFilepath, @@ -168,7 +196,7 @@ func (backend *DockerKurtosisBackend) CreateAPIContainer( } // TODO: configure the APIContainer to send the logs to the Fluentbit logs collector server - createAndStartArgs := docker_manager.NewCreateAndStartContainerArgsBuilder( + createAndStartArgsBuilder := docker_manager.NewCreateAndStartContainerArgsBuilder( image, apiContainerAttrs.GetName().GetString(), enclaveNetwork.GetId(), @@ -184,7 +212,23 @@ func (backend *DockerKurtosisBackend) CreateAPIContainer( ipAddr, ).WithLabels( labelStrs, - ).WithRestartPolicy(docker_manager.RestartOnFailure).Build() + ).WithRestartPolicy(docker_manager.RestartOnFailure) + + if shouldStartInDebugMode { + // Adding systrace capabilities when starting the debug server in the engine's container + capabilities := map[docker_manager.ContainerCapability]bool{ + docker_manager.SysPtrace: true, + } + createAndStartArgsBuilder.WithAddedCapabilities(capabilities) + + // Setting security for debugging the engine's container + securityOpts := map[docker_manager.ContainerSecurityOpt]bool{ + docker_manager.AppArmorUnconfined: true, + } + createAndStartArgsBuilder.WithSecurityOpts(securityOpts) + } + + createAndStartArgs := createAndStartArgsBuilder.Build() if _, err = backend.dockerManager.FetchImageIfMissing(ctx, image, emptyRegistrySpecAsPublicImage); err != nil { logrus.Warnf("Failed to pull the latest version of API container image '%v'; you may be running an out-of-date version. Error:\n%v", image, err) diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go index 814e50ebf8..2d6fafa414 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go @@ -74,6 +74,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( enclaveDataVolumeDirpath string, ownIpAddressEnvVar string, customEnvVars map[string]string, + shouldStartInDebugMode bool, ) ( *api_container.APIContainer, error, diff --git a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go index 87d98da01a..dcdbbf3b74 100644 --- a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go @@ -200,6 +200,7 @@ func (backend *MetricsReportingKurtosisBackend) CreateAPIContainer( enclaveDataVolumeDirpath string, ownIpEnvVar string, customEnvVars map[string]string, + shouldStartInDebugMode bool, ) (*api_container.APIContainer, error) { if _, found := customEnvVars[ownIpEnvVar]; found { return nil, stacktrace.NewError("Requested own IP environment variable '%v' conflicts with custom environment variable", ownIpEnvVar) @@ -213,6 +214,7 @@ func (backend *MetricsReportingKurtosisBackend) CreateAPIContainer( enclaveDataVolumeDirpath, ownIpEnvVar, customEnvVars, + shouldStartInDebugMode, ) if err != nil { // WARNING: remember not to print 'customEnvVars' because it could end up creating a secret info leak diff --git a/container-engine-lib/lib/backend_interface/kurtosis_backend.go b/container-engine-lib/lib/backend_interface/kurtosis_backend.go index d7b655d70b..0e23bf4f8e 100644 --- a/container-engine-lib/lib/backend_interface/kurtosis_backend.go +++ b/container-engine-lib/lib/backend_interface/kurtosis_backend.go @@ -140,6 +140,7 @@ type KurtosisBackend interface { // Must not conflict with the custom environment variables ownIpAddressEnvVar string, customEnvVars map[string]string, + shouldStartInDebugMode bool, ) ( *api_container.APIContainer, error, diff --git a/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go b/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go index dd68bb93fd..554300cdae 100644 --- a/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go @@ -156,9 +156,13 @@ func (_c *MockKurtosisBackend_CopyFilesFromUserService_Call) RunAndReturn(run fu return _c } -// CreateAPIContainer provides a mock function with given fields: ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars -func (_m *MockKurtosisBackend) CreateAPIContainer(ctx context.Context, image string, enclaveUuid enclave.EnclaveUUID, grpcPortNum uint16, enclaveDataVolumeDirpath string, ownIpAddressEnvVar string, customEnvVars map[string]string) (*api_container.APIContainer, error) { - ret := _m.Called(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars) +// CreateAPIContainer provides a mock function with given fields: ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars, shouldStartInDebugMode +func (_m *MockKurtosisBackend) CreateAPIContainer(ctx context.Context, image string, enclaveUuid enclave.EnclaveUUID, grpcPortNum uint16, enclaveDataVolumeDirpath string, ownIpAddressEnvVar string, customEnvVars map[string]string, shouldStartInDebugMode bool) (*api_container.APIContainer, error) { + ret := _m.Called(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars, shouldStartInDebugMode) + + if len(ret) == 0 { + panic("no return value specified for CreateAPIContainer") + } if len(ret) == 0 { panic("no return value specified for CreateAPIContainer") @@ -166,19 +170,19 @@ func (_m *MockKurtosisBackend) CreateAPIContainer(ctx context.Context, image str var r0 *api_container.APIContainer var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string) (*api_container.APIContainer, error)); ok { - return rf(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars) + if rf, ok := ret.Get(0).(func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string, bool) (*api_container.APIContainer, error)); ok { + return rf(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars, shouldStartInDebugMode) } - if rf, ok := ret.Get(0).(func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string) *api_container.APIContainer); ok { - r0 = rf(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars) + if rf, ok := ret.Get(0).(func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string, bool) *api_container.APIContainer); ok { + r0 = rf(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars, shouldStartInDebugMode) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*api_container.APIContainer) } } - if rf, ok := ret.Get(1).(func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string) error); ok { - r1 = rf(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars) + if rf, ok := ret.Get(1).(func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string, bool) error); ok { + r1 = rf(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars, shouldStartInDebugMode) } else { r1 = ret.Error(1) } @@ -199,13 +203,14 @@ type MockKurtosisBackend_CreateAPIContainer_Call struct { // - enclaveDataVolumeDirpath string // - ownIpAddressEnvVar string // - customEnvVars map[string]string -func (_e *MockKurtosisBackend_Expecter) CreateAPIContainer(ctx interface{}, image interface{}, enclaveUuid interface{}, grpcPortNum interface{}, enclaveDataVolumeDirpath interface{}, ownIpAddressEnvVar interface{}, customEnvVars interface{}) *MockKurtosisBackend_CreateAPIContainer_Call { - return &MockKurtosisBackend_CreateAPIContainer_Call{Call: _e.mock.On("CreateAPIContainer", ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars)} +// - shouldStartInDebugMode bool +func (_e *MockKurtosisBackend_Expecter) CreateAPIContainer(ctx interface{}, image interface{}, enclaveUuid interface{}, grpcPortNum interface{}, enclaveDataVolumeDirpath interface{}, ownIpAddressEnvVar interface{}, customEnvVars interface{}, shouldStartInDebugMode interface{}) *MockKurtosisBackend_CreateAPIContainer_Call { + return &MockKurtosisBackend_CreateAPIContainer_Call{Call: _e.mock.On("CreateAPIContainer", ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars, shouldStartInDebugMode)} } -func (_c *MockKurtosisBackend_CreateAPIContainer_Call) Run(run func(ctx context.Context, image string, enclaveUuid enclave.EnclaveUUID, grpcPortNum uint16, enclaveDataVolumeDirpath string, ownIpAddressEnvVar string, customEnvVars map[string]string)) *MockKurtosisBackend_CreateAPIContainer_Call { +func (_c *MockKurtosisBackend_CreateAPIContainer_Call) Run(run func(ctx context.Context, image string, enclaveUuid enclave.EnclaveUUID, grpcPortNum uint16, enclaveDataVolumeDirpath string, ownIpAddressEnvVar string, customEnvVars map[string]string, shouldStartInDebugMode bool)) *MockKurtosisBackend_CreateAPIContainer_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(enclave.EnclaveUUID), args[3].(uint16), args[4].(string), args[5].(string), args[6].(map[string]string)) + run(args[0].(context.Context), args[1].(string), args[2].(enclave.EnclaveUUID), args[3].(uint16), args[4].(string), args[5].(string), args[6].(map[string]string), args[7].(bool)) }) return _c } @@ -215,7 +220,7 @@ func (_c *MockKurtosisBackend_CreateAPIContainer_Call) Return(_a0 *api_container return _c } -func (_c *MockKurtosisBackend_CreateAPIContainer_Call) RunAndReturn(run func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string) (*api_container.APIContainer, error)) *MockKurtosisBackend_CreateAPIContainer_Call { +func (_c *MockKurtosisBackend_CreateAPIContainer_Call) RunAndReturn(run func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string, bool) (*api_container.APIContainer, error)) *MockKurtosisBackend_CreateAPIContainer_Call { _c.Call.Return(run) return _c } diff --git a/core/launcher/api_container_launcher/api_container_launcher.go b/core/launcher/api_container_launcher/api_container_launcher.go index f95583467a..9f49cd3943 100644 --- a/core/launcher/api_container_launcher/api_container_launcher.go +++ b/core/launcher/api_container_launcher/api_container_launcher.go @@ -45,6 +45,7 @@ func (launcher ApiContainerLauncher) LaunchWithDefaultVersion( isCI bool, cloudUserID metrics_client.CloudUserID, cloudInstanceID metrics_client.CloudInstanceID, + shouldStartInDebugMode bool, ) ( resultApiContainer *api_container.APIContainer, resultErr error, @@ -63,6 +64,7 @@ func (launcher ApiContainerLauncher) LaunchWithDefaultVersion( isCI, cloudUserID, cloudInstanceID, + shouldStartInDebugMode, ) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred launching the API container with default version tag '%v'", kurtosis_version.KurtosisVersion) @@ -84,6 +86,7 @@ func (launcher ApiContainerLauncher) LaunchWithCustomVersion( isCI bool, cloudUserID metrics_client.CloudUserID, cloudInstanceID metrics_client.CloudInstanceID, + shouldStartInDebugMode bool, ) ( resultApiContainer *api_container.APIContainer, resultErr error, @@ -129,6 +132,7 @@ func (launcher ApiContainerLauncher) LaunchWithCustomVersion( enclaveDataVolumeDirpath, ownIpAddressEnvvar, envVars, + shouldStartInDebugMode, ) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred launching the API container") diff --git a/core/scripts/build.sh b/core/scripts/build.sh index 02282fdd51..8f9bbe2991 100755 --- a/core/scripts/build.sh +++ b/core/scripts/build.sh @@ -10,19 +10,41 @@ root_dirpath="$(dirname "${script_dirpath}")" # ================================================================================================== # Constants # ================================================================================================== +DEFAULT_DEBUG_IMAGE=false +SET_ARCH_AUTOMATICALLY="" +DEFAULT_SKIP_DOCKER_IMAGE_BUILDING=false + BUILD_SCRIPT_RELATIVE_FILEPATHS=( "launcher/scripts/build.sh" "server/scripts/build.sh" "files_artifacts_expander/scripts/build.sh" ) +# ================================================================================================== +# Arg Parsing & Validation +# ================================================================================================== +show_helptext_and_exit() { + echo "Usage: $(basename "${0}") debug_image..." + echo "" + echo " debug_image Whether images should contains the debug server and run in debug mode, this will use the Dockerfile.debug image to build the container (configured for the APIC server so far)" + echo "" + exit 1 # Exit with an error so that if this is accidentally called by CI, the script will fail +} + +# Raw parsing of the arguments +debug_image="${1:-"${DEFAULT_DEBUG_IMAGE}"}" +if [ "${debug_image}" != "true" ] && [ "${debug_image}" != "false" ]; then + echo "Error: Invalid debug_image arg: '${debug_image}'" >&2 + show_helptext_and_exit +fi + # ================================================================================================== # Main Logic # ================================================================================================== for build_script_rel_filepath in "${BUILD_SCRIPT_RELATIVE_FILEPATHS[@]}"; do build_script_abs_filepath="${root_dirpath}/${build_script_rel_filepath}" - if ! bash "${build_script_abs_filepath}"; then + if ! bash "${build_script_abs_filepath}" "${DEFAULT_SKIP_DOCKER_IMAGE_BUILDING}" "${SET_ARCH_AUTOMATICALLY}" "${debug_image}"; then echo "Error: Build script '${build_script_abs_filepath}' failed" >&2 exit 1 fi diff --git a/core/server/Dockerfile.debug b/core/server/Dockerfile.debug new file mode 100644 index 0000000000..a8b268dc55 --- /dev/null +++ b/core/server/Dockerfile.debug @@ -0,0 +1,18 @@ +FROM alpine:3.19 + +# We need protobut-dev to run protobuf compiler against startosis .proto files +RUN apk update && apk add --no-cache bash protobuf-dev + +# Make sure that you changed the port inside the APIC's code before changing it here +EXPOSE 50103 + +ARG TARGETARCH + +WORKDIR /run + +COPY ./build/api-container.$TARGETARCH ./api-container + +COPY ./build/dlv.$TARGETARCH /dlv + +# Make sure that you changed the port inside the engine's code before changing it here +CMD ["/dlv", "--listen=:50103", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "--continue", "./api-container"] diff --git a/core/server/scripts/_constants.env b/core/server/scripts/_constants.env index e8e6258638..d151d2fe13 100644 --- a/core/server/scripts/_constants.env +++ b/core/server/scripts/_constants.env @@ -8,3 +8,11 @@ # constant in the 'launcher' submodule!! IMAGE_ORG_AND_REPO="kurtosistech/core" # ^^^^^^^^^^^^^^^^ WARNING ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +BUILD_DIRNAME="build" +DEFAULT_ARCHITECTURE_TO_BUILD="unknown" +DEFAULT_SKIP_DOCKER_IMAGE_BUILDING=false +DOCKER_IMAGE_FILENAME="Dockerfile" +DOCKER_DEBUG_IMAGE_FILENAME="${DOCKER_IMAGE_FILENAME}.debug" +DOCKER_DEBUG_IMAGE_NAME_SUFFIX="debug" +DEFAULT_DEBUG_IMAGE=false diff --git a/core/server/scripts/build.sh b/core/server/scripts/build.sh index 0930255de5..81b8f70a6f 100755 --- a/core/server/scripts/build.sh +++ b/core/server/scripts/build.sh @@ -11,12 +11,6 @@ uname_arch=$(uname -m) # ================================================================================================== source "${script_dirpath}/_constants.env" -BUILD_DIRNAME="build" - -DEFAULT_SKIP_DOCKER_IMAGE_BUILDING=false - -DEFAULT_ARCHITECTURE_TO_BUILD="unknown" - if [ "$uname_arch" == "x86_64" ] || [ "$uname_arch" == "amd64" ]; then DEFAULT_ARCHITECTURE_TO_BUILD="amd64" elif [ "$uname_arch" == "aarch64" ] || [ "$uname_arch" == "arm64" ]; then @@ -28,9 +22,23 @@ MAIN_GO_FILEPATH="${server_root_dirpath}/${MAIN_DIRNAME}/main.go" MAIN_BINARY_OUTPUT_FILENAME="api-container" MAIN_BINARY_OUTPUT_FILEPATH="${server_root_dirpath}/${BUILD_DIRNAME}/${MAIN_BINARY_OUTPUT_FILENAME}" -# ============================================================================= -# Main Code -# ============================================================================= +DELVE_BINARY_FILENAME="dlv" +DELVE_BINARY_OUTPUT_FILEPATH="${server_root_dirpath}/${BUILD_DIRNAME}/${DELVE_BINARY_FILENAME}" + +# ================================================================================================== +# Arg Parsing & Validation +# ================================================================================================== +show_helptext_and_exit() { + echo "Usage: $(basename "${0}") skip_docker_image_building, architecture_to_build, debug_image..." + echo "" + echo " skip_docker_image_building Whether build the Docker image" + echo " architecture_to_build The desired architecture for the project's binaries" + echo " debug_image Whether images should contains the debug server and run in debug mode, this will use the Dockerfile.debug image to build the container" + echo "" + exit 1 # Exit with an error so that if this is accidentally called by CI, the script will fail +} + + skip_docker_image_building="${1:-"${DEFAULT_SKIP_DOCKER_IMAGE_BUILDING}"}" if [ "${skip_docker_image_building}" != "true" ] && [ "${skip_docker_image_building}" != "false" ]; then echo "Error: Invalid skip-docker-image-building arg '${skip_docker_image_building}'" >&2 @@ -41,6 +49,15 @@ if [ "${architecture_to_build}" != "amd64" ] && [ "${architecture_to_build}" != echo "Error: Invalid architecture-to-build arg '${architecture_to_build}'" >&2 fi +debug_image="${3:-"${DEFAULT_DEBUG_IMAGE}"}" +if [ "${debug_image}" != "true" ] && [ "${debug_image}" != "false" ]; then + echo "Error: Invalid debug_image arg: '${debug_image}'" >&2 + show_helptext_and_exit +fi + +# ============================================================================= +# Main Code +# ============================================================================= # Checks if dockerignore file is in the root path if ! [ -f "${server_root_dirpath}"/.dockerignore ]; then echo "Error: No .dockerignore file found in server root '${server_root_dirpath}'; this is required so Docker caching is enabled and the image builds remain quick" >&2 @@ -61,12 +78,46 @@ echo "Tests succeeded" # Build binary for packaging inside an Alpine Linux image echo "Building server main.go '${MAIN_GO_FILEPATH}'..." -if ! CGO_ENABLED=0 GOOS=linux GOARCH=${architecture_to_build} go build -o "${MAIN_BINARY_OUTPUT_FILEPATH}.${architecture_to_build}" "${MAIN_GO_FILEPATH}"; then +gc_flags="" +if "${debug_image}"; then + gc_flags="all=-N -l" +fi +if ! CGO_ENABLED=0 GOOS=linux GOARCH=${architecture_to_build} go build -gcflags="${gc_flags}" -o "${MAIN_BINARY_OUTPUT_FILEPATH}.${architecture_to_build}" "${MAIN_GO_FILEPATH}"; then echo "Error: An error occurred building the server code" >&2 exit 1 fi echo "Successfully built server code" +# TODO move this to its own file because it's duplicated in the engine's build script +# Install and copy delve (the server debugger: https://github.com/go-delve/delve) to include it into the build folder so it can be picked up from the Docker image file +if "${debug_image}"; then + # Checks if the binary already exist + delve_exist="false" + dlv_go_folder_bin_filepath=~/go/bin/linux_"${architecture_to_build}"/"${DELVE_BINARY_FILENAME}" + dlv_bin_in_project_filepath="${DELVE_BINARY_OUTPUT_FILEPATH}.${architecture_to_build}" + # Check if it is in the build folder and skip if it is there + if ! [ -f "$dlv_bin_in_project_filepath" ]; then + # Check if it already exist in the goland folder + if ! [ -f "$dlv_go_folder_bin_filepath" ]; then + echo "Installing delve..." + if ! GOOS=linux GOARCH=${architecture_to_build} go install "${DELVE_SERVER_REPOSITORY_AND_VERSION}"; then + echo "Error: An error occurred installing the delve binary" >&2 + exit 1 + fi + echo "...delve successfully installed." + fi + # Now checks if the binary has been created + if ! [ -f "$dlv_go_folder_bin_filepath" ]; then + echo "Error: expected to have dlv binary in ${dlv_go_folder_bin_filepath} but it is not there." + exit 1 + fi + if ! cp "$dlv_go_folder_bin_filepath" "$dlv_bin_in_project_filepath" ; then + echo "Error: something failed while copying the delve binary from the GO bin folder to the project's build folder" + exit 1 + fi + fi +fi + # Generate Docker image tag if ! cd "${git_repo_dirpath}"; then echo "Error: Couldn't cd to the git root dirpath '${git_repo_dirpath}'" >&2 @@ -83,8 +134,19 @@ if "${skip_docker_image_building}"; then exit 0 fi -dockerfile_filepath="${server_root_dirpath}/Dockerfile" +dockerfile_filepath="${server_root_dirpath}/${DOCKER_IMAGE_FILENAME}" + +# Using the Docker debug image if it was requested +if "${debug_image}"; then + dockerfile_filepath="${server_root_dirpath}/${DOCKER_DEBUG_IMAGE_FILENAME}" +fi + image_name="${IMAGE_ORG_AND_REPO}:${docker_tag}" +# specifying that this is a image for debugging +if "${debug_image}"; then + image_name="${image_name}-${DOCKER_DEBUG_IMAGE_NAME_SUFFIX}" +fi + load_not_push_image=false docker_build_script_cmd="${git_repo_dirpath}/scripts/docker-image-builder.sh ${load_not_push_image} ${dockerfile_filepath} ${image_name}" if ! eval "${docker_build_script_cmd}"; then diff --git a/engine/scripts/build.sh b/engine/scripts/build.sh index b1c43e013b..136daecaee 100755 --- a/engine/scripts/build.sh +++ b/engine/scripts/build.sh @@ -25,7 +25,7 @@ BUILD_SCRIPT_RELATIVE_FILEPATHS=( show_helptext_and_exit() { echo "Usage: $(basename "${0}") debug_image..." echo "" - echo " debug_image Whether images should contains the debug server and run in debug mode, this will use the Dockerfile.debug image to build the container (only configured for the engine server so far)" + echo " debug_image Whether images should contains the debug server and run in debug mode, this will use the Dockerfile.debug image to build the container (configured for the engine server so far)" echo "" exit 1 # Exit with an error so that if this is accidentally called by CI, the script will fail } diff --git a/engine/server/engine/enclave_manager/enclave_creator.go b/engine/server/engine/enclave_manager/enclave_creator.go index c8708a1f8f..523c898da9 100644 --- a/engine/server/engine/enclave_manager/enclave_creator.go +++ b/engine/server/engine/enclave_manager/enclave_creator.go @@ -51,6 +51,7 @@ func (creator *EnclaveCreator) CreateEnclave( cloudUserID metrics_client.CloudUserID, cloudInstanceID metrics_client.CloudInstanceID, kurtosisBackendType args.KurtosisBackendType, + shouldAPICRunInDebugMode bool, ) (*types.EnclaveInfo, error) { uuid, err := uuid_generator.GenerateUUIDString() @@ -112,6 +113,7 @@ func (creator *EnclaveCreator) CreateEnclave( isCI, cloudUserID, cloudInstanceID, + shouldAPICRunInDebugMode, ) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred launching the API container") @@ -198,6 +200,7 @@ func (creator *EnclaveCreator) launchApiContainer( isCI bool, cloudUserID metrics_client.CloudUserID, cloudInstanceID metrics_client.CloudInstanceID, + shouldStartInDebugMode bool, ) ( resultApiContainer *api_container.APIContainer, resultErr error, @@ -220,6 +223,7 @@ func (creator *EnclaveCreator) launchApiContainer( isCI, cloudUserID, cloudInstanceID, + shouldStartInDebugMode, ) 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) @@ -239,6 +243,7 @@ func (creator *EnclaveCreator) launchApiContainer( isCI, cloudUserID, cloudInstanceID, + shouldStartInDebugMode, ) 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 91ef2c8058..d966dd47bd 100644 --- a/engine/server/engine/enclave_manager/enclave_manager.go +++ b/engine/server/engine/enclave_manager/enclave_manager.go @@ -137,6 +137,7 @@ func (manager *EnclaveManager) CreateEnclave( //If blank, will use a random one enclaveName string, isProduction bool, + shouldAPICRunInDebugMode bool, ) (*types.EnclaveInfo, error) { manager.mutex.Lock() defer manager.mutex.Unlock() @@ -173,6 +174,7 @@ func (manager *EnclaveManager) CreateEnclave( engineVersion, apiContainerImageVersionTag, apiContainerLogLevel, + shouldAPICRunInDebugMode, ) if err != nil { logrus.Errorf("An error occurred when trying to get an enclave from the enclave pool. Err:\n%v", err) @@ -193,6 +195,7 @@ func (manager *EnclaveManager) CreateEnclave( manager.cloudUserID, manager.cloudInstanceID, manager.kurtosisBackendType, + shouldAPICRunInDebugMode, ) 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 bf97ea2e04..459e7a36e4 100644 --- a/engine/server/engine/enclave_manager/enclave_pool.go +++ b/engine/server/engine/enclave_manager/enclave_pool.go @@ -22,6 +22,8 @@ const ( fill = true createTestEnclave = false + + defaultApicDebugModeForEnclavesInThePool = false ) type EnclavePool struct { @@ -122,6 +124,7 @@ func (pool *EnclavePool) GetEnclave( engineVersion string, apiContainerVersion string, apiContainerLogLevel logrus.Level, + shouldAPICRunInDebugMode bool, ) (*types.EnclaveInfo, error) { logrus.Debugf( @@ -139,6 +142,7 @@ func (pool *EnclavePool) GetEnclave( engineVersion, apiContainerVersion, apiContainerLogLevel, + shouldAPICRunInDebugMode, ) { logrus.Debugf("The requested enclave params are different from the enclave in the pool params") return nil, nil @@ -291,6 +295,7 @@ func (pool *EnclavePool) createNewIdleEnclave(ctx context.Context) (*types.Encla pool.cloudUserID, pool.cloudInstanceID, args.KurtosisBackendType_Kubernetes, // enclave pool only available for k8s + defaultApicDebugModeForEnclavesInThePool, ) if err != nil { return nil, stacktrace.Propagate( @@ -348,6 +353,7 @@ func areRequestedEnclaveParamsEqualToEnclaveInThePoolParams( engineVersion string, apiContainerVersion string, apiContainerLogLevel logrus.Level, + shouldAPICRunInDebugMode bool, ) bool { // if the api container version is empty string means that will be executed with the default version @@ -357,7 +363,7 @@ func areRequestedEnclaveParamsEqualToEnclaveInThePoolParams( } if engineVersion == apiContainerVersion && - apiContainerLogLevel == defaultApiContainerLogLevel { + apiContainerLogLevel == defaultApiContainerLogLevel && !shouldAPICRunInDebugMode { return true } diff --git a/engine/server/engine/server/engine_connect_server_service.go b/engine/server/engine/server/engine_connect_server_service.go index 29a48b8749..6dc9804e6b 100644 --- a/engine/server/engine/server/engine_connect_server_service.go +++ b/engine/server/engine/server/engine_connect_server_service.go @@ -187,6 +187,7 @@ func (service *EngineConnectServerService) CreateEnclave(ctx context.Context, co apiContainerLogLevel, args.GetEnclaveName(), isProduction, + args.GetShouldApicRunInDebugMode(), ) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred creating new enclave with name '%v'", args.GetEnclaveName()) diff --git a/engine/server/engine/server/engine_rest_api_handler.go b/engine/server/engine/server/engine_rest_api_handler.go index 7af6f7a973..e7f9665024 100644 --- a/engine/server/engine/server/engine_rest_api_handler.go +++ b/engine/server/engine/server/engine_rest_api_handler.go @@ -74,6 +74,7 @@ func (engine EngineRuntime) PostEnclaves(ctx context.Context, request api.PostEn enclaveMode := utils.DerefWith(request.Body.Mode, api_type.TEST) enclaveName := request.Body.EnclaveName apicVersionTag := request.Body.ApiContainerVersionTag + shouldApicRunInDebugMode := utils.DerefWith(request.Body.ShouldApicRunInDebugMode, api_type.False) if err := engine.MetricsClient.TrackCreateEnclave(enclaveName, subnetworkDisableBecauseItIsDeprecated); err != nil { logrus.Warn("An error occurred while logging the create enclave event") @@ -106,6 +107,7 @@ func (engine EngineRuntime) PostEnclaves(ctx context.Context, request api.PostEn apiContainerLogLevel, enclaveName, isProduction, + bool(shouldApicRunInDebugMode), ) if err != nil { response := internalErrorResponseInfof(err, "An error occurred creating new enclave with name '%v'", request.Body.EnclaveName) diff --git a/internal_testsuites/typescript/yarn.lock b/internal_testsuites/typescript/yarn.lock index a77aeec02a..b9faf1d50b 100644 --- a/internal_testsuites/typescript/yarn.lock +++ b/internal_testsuites/typescript/yarn.lock @@ -10,68 +10,58 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" - integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== dependencies: - "@babel/highlight" "^7.22.13" + "@babel/highlight" "^7.23.4" chalk "^2.4.2" -"@babel/compat-data@^7.22.9": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.20.tgz#8df6e96661209623f1975d66c35ffca66f3306d0" - integrity sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw== +"@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.20.tgz#e3d0eed84c049e2a2ae0a64d27b6a37edec385b7" - integrity sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA== + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1" + integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.22.15" - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-module-transforms" "^7.22.20" - "@babel/helpers" "^7.22.15" - "@babel/parser" "^7.22.16" - "@babel/template" "^7.22.15" - "@babel/traverse" "^7.22.20" - "@babel/types" "^7.22.19" - convert-source-map "^1.7.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.9" + "@babel/parser" "^7.23.9" + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.22.15", "@babel/generator@^7.7.2": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.15.tgz#1564189c7ec94cb8f77b5e8a90c4d200d21b2339" - integrity sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA== - dependencies: - "@babel/types" "^7.22.15" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" - integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== +"@babel/generator@^7.23.6", "@babel/generator@^7.7.2": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== dependencies: - "@babel/types" "^7.23.0" + "@babel/types" "^7.23.6" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-compilation-targets@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" - integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== dependencies: - "@babel/compat-data" "^7.22.9" - "@babel/helper-validator-option" "^7.22.15" - browserslist "^4.21.9" + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" lru-cache "^5.1.1" semver "^6.3.1" @@ -102,10 +92,10 @@ dependencies: "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz#da9edc14794babbe7386df438f3768067132f59e" - integrity sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A== +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== dependencies: "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-module-imports" "^7.22.15" @@ -132,48 +122,43 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== -"@babel/helper-validator-identifier@^7.22.19", "@babel/helper-validator-identifier@^7.22.20": +"@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helper-validator-option@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" - integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== -"@babel/helpers@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.15.tgz#f09c3df31e86e3ea0b7ff7556d85cdebd47ea6f1" - integrity sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw== +"@babel/helpers@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.9.tgz#c3e20bbe7f7a7e10cb9b178384b4affdf5995c7d" + integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== dependencies: - "@babel/template" "^7.22.15" - "@babel/traverse" "^7.22.15" - "@babel/types" "^7.22.15" + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" -"@babel/highlight@^7.22.13": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" - integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== dependencies: "@babel/helper-validator-identifier" "^7.22.20" chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.22.16": - version "7.22.16" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95" - integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA== - -"@babel/parser@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" - integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" + integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -260,52 +245,43 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272" - integrity sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" + integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/template@^7.22.15", "@babel/template@^7.3.3": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== +"@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.3.3": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" + integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" -"@babel/traverse@^7.22.15", "@babel/traverse@^7.22.20", "@babel/traverse@^7.7.2": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" - integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== +"@babel/traverse@^7.23.9", "@babel/traverse@^7.7.2": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" + integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.0" - "@babel/types" "^7.23.0" - debug "^4.1.0" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" + debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.3.3": - version "7.22.19" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.19.tgz#7425343253556916e440e662bb221a93ddb75684" - integrity sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.3.3": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" + integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" - integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== - dependencies: - "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-string-parser" "^7.23.4" "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" @@ -327,14 +303,14 @@ integrity sha512-rYn3Akp7teOkvqxguLbf6QKizH37Yeo33lseV+JHDZC19CsAV9wrrZM17Sa+LNEBj/hrYtQF7EIcllgGxhv9aw== "@bufbuild/protobuf@^1.3.0": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-1.3.1.tgz#c4de66bacbe7ac97fe054e68314aeba6f45177f9" - integrity sha512-BUyJWutgP2S8K/1NphOJokuwDckXS4qI2T1pGZAlkFdZchWae3jm6fCdkcGbLlM1QLOcNFFePd+7Feo4BYGrJQ== + version "1.7.2" + resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-1.7.2.tgz#1b3d6c66ebd987f7da7f1e3f0546cffaa87f8732" + integrity sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ== "@grpc/grpc-js@^1.4.4": - version "1.9.3" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.3.tgz#811cc49966ab7ed96efa31d213e80d671fd13839" - integrity sha512-b8iWtdrYIeT5fdZdS4Br/6h/kuk0PW5EVBUGk1amSbrpL8DlktJD43CdcCWwRdd6+jgwHhADSbL9CsNnm6EUPA== + version "1.9.14" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.14.tgz#236378822876cbf7903f9d61a0330410e8dcc5a1" + integrity sha512-nOpuzZ2G3IuMFN+UPPpKrC6NsLmWsTqSsm66IRfnBt1D4pwTqE27lmbpcPM+l2Ua4gE7PfjRHI6uedAy7hoXUw== dependencies: "@grpc/proto-loader" "^0.7.8" "@types/node" ">=12.12.47" @@ -604,9 +580,9 @@ integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.19" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" - integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" @@ -694,9 +670,9 @@ integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.2.tgz#215db4f4a35d710256579784a548907237728756" - integrity sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" @@ -705,85 +681,87 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.5.tgz#281f4764bcbbbc51fdded0f25aa587b4ce14da95" - integrity sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w== + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.4.2" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.2.tgz#843e9f1f47c957553b0c374481dc4772921d6a6b" - integrity sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ== + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.2.tgz#4ddf99d95cfdd946ff35d2b65c978d9c9bf2645d" - integrity sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" + integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== dependencies: "@babel/types" "^7.20.7" "@types/google-protobuf@^3.15.5": - version "3.15.7" - resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.7.tgz#882e7351bc8ccf30bb21c507cc4597cf76f8888b" - integrity sha512-pIEMnb04J60c5eExVLUY/R4eWT5QEQ5cC792JOSfDI3kLjaKC4TjdgMp3xIrN1vxbi2Zk8LcscTm0VaNrIdniA== + version "3.15.12" + resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.12.tgz#eb2ba0eddd65712211a2b455dc6071d665ccf49b" + integrity sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ== "@types/graceful-fs@^4.1.2": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.7.tgz#30443a2e64fd51113bc3e2ba0914d47109695e2a" - integrity sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw== + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== dependencies: "@types/node" "*" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" "@types/jest@^29.0.0": - version "29.5.5" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.5.tgz#727204e06228fe24373df9bae76b90f3e8236a2a" - integrity sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg== + version "29.5.12" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" + integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== dependencies: expect "^29.0.0" pretty-format "^29.0.0" "@types/js-yaml@^4.0.5": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.6.tgz#4b3afd5158b8749095b1f096967b6d0f838d862f" - integrity sha512-ACTuifTSIIbyksx2HTon3aFtCKWcID7/h3XEmRpDYdMCXxPbl+m9GteOJeaAkiAta/NJaSFuA7ahZ0NkwajDSw== + version "4.0.9" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" + integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== "@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": - version "20.6.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.3.tgz#5b763b321cd3b80f6b8dde7a37e1a77ff9358dd9" - integrity sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA== + version "20.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.16.tgz#4411f79411514eb8e2926f036c86c9f0e4ec6708" + integrity sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ== + dependencies: + undici-types "~5.26.4" "@types/node@^16.11.11": - version "16.18.53" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.53.tgz#21820fe4d5968aaf8071dabd1ee13d24ada1350a" - integrity sha512-vVmHeo4tpF8zsknALU90Hh24VueYdu45ZlXzYWFbom61YR4avJqTFDC3QlWzjuTdAv6/3xHaxiO9NrtVZXrkmw== + version "16.18.79" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.79.tgz#153e25561b271cf87dc1b28d38f98cebd514d788" + integrity sha512-Qd7jdLR5zmnIyMhfDrfPqN5tUCvreVpP3Qrf2oSM+F7SNzlb/MwHISGUkdFHtevfkPJ3iAGyeQI/jsbh9EStgQ== "@types/path-browserify@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/path-browserify/-/path-browserify-1.0.0.tgz#294ec6e88b6b0d340a3897b7120e5b393f16690e" - integrity sha512-XMCcyhSvxcch8b7rZAtFAaierBYdeHXVvg2iYnxOV0MCQHmPuRRmGZPFDRzPayxcGiiSL1Te9UIO+f3cuj0tfw== + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/path-browserify/-/path-browserify-1.0.2.tgz#14b62d4371316016f254b9cca5c10e60ef33dcc0" + integrity sha512-ZkC5IUqqIFPXx3ASTTybTzmQdwHwe2C0u3eL75ldQ6T9E9IWFJodn6hIfbZGab73DfyiHN4Xw15gNxUq2FbvBA== "@types/prettier@^2.1.5": version "2.7.3" @@ -791,36 +769,36 @@ integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== "@types/semver@^7.3.9": - version "7.5.2" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564" - integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw== + version "7.5.6" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" + integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== "@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/utf8@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/utf8/-/utf8-3.0.1.tgz#bf081663d4fff05ee63b41f377a35f8b189f7e5b" - integrity sha512-1EkWuw7rT3BMz2HpmcEOr/HL61mWNA6Ulr/KdbXR9AI0A55wD4Qfv8hizd8Q1DnknSIzzDvQmvvY/guvX7jjZA== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/utf8/-/utf8-3.0.3.tgz#e153ada15157477a9e0d2bcc638c34284a1d82ea" + integrity sha512-+lqLGxWZsEe4Z6OrzBI7Ym4SMUTaMS5yOrHZ0/IL0bpIye1Qbs4PpobJL2mLDbftUXlPFZR7fu6d1yM+bHLX1w== "@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^16.0.0": - version "16.0.5" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" - integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== + version "16.0.9" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e" + integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== dependencies: "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.24" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" - integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== dependencies: "@types/yargs-parser" "*" @@ -848,9 +826,9 @@ acorn@^7.1.1: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4: - version "8.10.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" - integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== agent-base@6: version "6.0.2" @@ -1013,15 +991,15 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.21.9: - version "4.21.10" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" - integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== +browserslist@^4.22.2: + version "4.22.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6" + integrity sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A== dependencies: - caniuse-lite "^1.0.30001517" - electron-to-chromium "^1.4.477" - node-releases "^2.0.13" - update-browserslist-db "^1.0.11" + caniuse-lite "^1.0.30001580" + electron-to-chromium "^1.4.648" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" bs-logger@0.x: version "0.2.6" @@ -1057,10 +1035,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001517: - version "1.0.30001538" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz#9dbc6b9af1ff06b5eb12350c2012b3af56744f3f" - integrity sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw== +caniuse-lite@^1.0.30001580: + version "1.0.30001583" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001583.tgz#abb2970cc370801dc7e27bf290509dc132cfa390" + integrity sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q== chalk@^2.4.2: version "2.4.2" @@ -1090,9 +1068,9 @@ chownr@^2.0.0: integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== ci-info@^3.2.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" - integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: version "1.2.3" @@ -1163,11 +1141,16 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.4.0, convert-source-map@^1.6.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1203,7 +1186,7 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1252,10 +1235,10 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -electron-to-chromium@^1.4.477: - version "1.4.526" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.526.tgz#1bcda5f2b8238e497c20fcdb41af5da907a770e2" - integrity sha512-tjjTMjmZAx1g6COrintLTa2/jcafYKxKoiEkdQOrVdbLaHh2wCt2nsAF8ZHweezkrP+dl/VG9T5nabcYoo0U5Q== +electron-to-chromium@^1.4.648: + version "1.4.655" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.655.tgz#112410db0d7f9c2b4ed8baa3b1b548522a6f89d4" + integrity sha512-2yszojF7vIZ68adIOvzV4bku8OZad9w5H9xF3ZAMZjPuOjBarlflUkjN6DggdV+L71WZuKUfKUhov/34+G5QHg== emittery@^0.8.1: version "0.8.1" @@ -1404,9 +1387,9 @@ find-up@^4.0.0, find-up@^4.1.0: path-exists "^4.0.0" follow-redirects@^1.14.4: - version "1.15.4" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" - integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== form-data@^3.0.0: version "3.0.1" @@ -1434,10 +1417,10 @@ fsevents@^2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" @@ -1487,9 +1470,9 @@ graceful-fs@^4.2.9: integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== grpc-web@^1.3.0, grpc-web@^1.3.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/grpc-web/-/grpc-web-1.4.2.tgz#86995f76471ce6b2119106ec26f909b7b69e7d43" - integrity sha512-gUxWq42l5ldaRplcKb4Pw5O4XBONWZgz3vxIIXnfIeJj8Jc3wYiq2O4c9xzx/NGbbPEej4rhI62C9eTENwLGNw== + version "1.5.0" + resolved "https://registry.yarnpkg.com/grpc-web/-/grpc-web-1.5.0.tgz#154e4007ab59a94bf7726b87ef6c5bd8815ecf6e" + integrity sha512-y1tS3BBIoiVSzKTDF3Hm7E8hV2n7YY7pO0Uo7depfWJqKzWE+SKr0jvHNIJsJJYILQlpYShpi/DRJJMbosgDMQ== has-flag@^3.0.0: version "3.0.0" @@ -1501,12 +1484,12 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== dependencies: - function-bind "^1.1.1" + function-bind "^1.1.2" html-encoding-sniffer@^2.0.1: version "2.0.1" @@ -1581,11 +1564,11 @@ is-arrayish@^0.2.1: integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-core-module@^2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" - integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" + hasown "^2.0.0" is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -1623,9 +1606,9 @@ isexe@^2.0.0: integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.2.1" @@ -2207,7 +2190,7 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== kurtosis-sdk@../../api/typescript: - version "0.86.1" + version "0.86.12" dependencies: "@bufbuild/connect" "^0.12.0" "@bufbuild/connect-web" "^0.12.0" @@ -2260,9 +2243,9 @@ lodash@^4.7.0: integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== loglevel@^1.7.1, loglevel@^1.8.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" - integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== + version "1.9.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.1.tgz#d63976ac9bcd03c7c873116d41c2a85bafff1be7" + integrity sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg== long@^5.0.0: version "5.2.3" @@ -2384,10 +2367,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" - integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== normalize-path@^3.0.0: version "3.0.0" @@ -2528,9 +2511,9 @@ prompts@^2.0.1: sisteransi "^1.0.5" protobufjs@^7.2.4: - version "7.2.5" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d" - integrity sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A== + version "7.2.6" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" + integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -2551,9 +2534,9 @@ psl@^1.1.33: integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== punycode@^2.1.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== querystringify@^2.1.1: version "2.2.0" @@ -2598,9 +2581,9 @@ resolve.exports@^1.1.0: integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== resolve@^1.20.0: - version "1.22.6" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" - integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: is-core-module "^2.13.0" path-parse "^1.0.7" @@ -2876,12 +2859,17 @@ typescript@^4.5.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== -update-browserslist-db@^1.0.11: +update-browserslist-db@^1.0.13: version "1.0.13" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== diff --git a/readme-static-files/goland-apic-breakpoint.png b/readme-static-files/goland-apic-breakpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..2f6f40580bde00100a332b473b93fae52d16ea65 GIT binary patch literal 44240 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 z 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 et6bSVP-4F-T%ZT#hy70gNBut?&2 + show_helptext_and_exit +fi + + +# ================================================================================================== +# Main Logic +# ================================================================================================== +apic_namespace_name="${KURTOSIS_KUBERNETES_NAMESPACE_PREFIX}-${enclave_name}" + +get_apic_pod_name_cmd="kubectl get pods -l="${APIC_CONTAINER_K8S_LABEL}" -n ${apic_namespace_name} | cut -d ' ' -f 1 | tail -n +2" + +# execute the port forward to the debug server inside the APIC's container +kubectl port-forward -n "${apic_namespace_name}" $(eval "${get_apic_pod_name_cmd}") ${APIC_DEBUG_SERVER_PORT}:${APIC_DEBUG_SERVER_PORT} From ffa3519b307dfbda83b67c9912d85ce55d0bcc14 Mon Sep 17 00:00:00 2001 From: Laurent Luce Date: Mon, 5 Feb 2024 12:35:27 -0500 Subject: [PATCH 022/102] chore: Update node js version to 20 in the dev setup steps (#2110) ## Description: Update node js version to 20 in the dev setup steps. ## Is this change user facing? NO --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ad13bf1161..7b6da21925 100644 --- a/README.md +++ b/README.md @@ -228,13 +228,13 @@ sudo apt update sudo apt install goreleaser ``` -#### Node (16.14 or above) and Yarn +#### Node (20.* or above) and Yarn On MacOS, using `NVM`: ```bash brew install nvm mkdir ~/.nvm -nvm install 16.14.0 +nvm install 20.11.0 npm install -g yarn ``` @@ -242,7 +242,7 @@ On Ubuntu, using `NVM`: ```bash curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash source ~/.bashrc -nvm install 16.14.0 +nvm install 20.11.0 npm install -g yarn ``` @@ -516,4 +516,4 @@ ktdev gateway [twitter]: https://twitter.com/KurtosisTech [starlark-explanation]: https://docs.kurtosis.com/explanations/starlark [stackoverflow-2022-developer-survey--other-tools]: https://survey.stackoverflow.co/2022/#most-popular-technologies-tools-tech-prof -[delve-docs]: https://github.com/go-delve/delve/blob/master/Documentation/cli/README.md \ No newline at end of file +[delve-docs]: https://github.com/go-delve/delve/blob/master/Documentation/cli/README.md From 1f738216851718bda1ebf9f9bf8936a715ae2cdf Mon Sep 17 00:00:00 2001 From: leoporoli Date: Mon, 5 Feb 2024 16:05:28 -0300 Subject: [PATCH 023/102] fix: adding the `core script build call`, which was removed by accident, in the main build script (#2118) ## Description: adding the `core script build call`, which was removed by accident, in the main build script ## Is this change user facing? NO ## References (if applicable): --- scripts/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/build.sh b/scripts/build.sh index f79dd383ca..d2c3cfc4eb 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -33,6 +33,7 @@ BUILD_SCRIPT_RELATIVE_FILEPATHS=( "metrics-library/scripts/build.sh" "enclave-manager/scripts/build.sh" "engine/scripts/build.sh" + "core/scripts/build.sh" ) # projects with debug mode enabled From 5a30ea75865bfc5fb9359d3da206124d13ebc45e Mon Sep 17 00:00:00 2001 From: Anders Schwartz Date: Mon, 5 Feb 2024 16:38:36 -0500 Subject: [PATCH 024/102] feat: add `env_vars` as input to `run_sh` (#2114) ## Description: This change adds `env_vars` to the run_sh. Feature was requested in issue #2050. Example of used in practice: ``` def run(plan, args={}): result = plan.run_sh( run = "mkdir -p kurtosis && echo $EXAMPLE", image = "badouralix/curl-jq", env_vars = { "EXAMPLE": "this_is_a_test" }, ) ``` outputs: ``` Container images used in this run: > badouralix/curl-jq - locally cached > run_sh run="mkdir -p kurtosis && echo $EXAMPLE" image="badouralix/curl-jq" env_vars={"EXAMPLE": "this_is_a_test"} Command returned with exit code '0' and the following output: -------------------- this_is_a_test -------------------- Starlark code successfully run. No output was returned. ``` ## Is this change user facing? YES ## References (if applicable): Closes: #2050 --- .../kurtosis_instruction/tasks/run_python.go | 7 ++++- .../kurtosis_instruction/tasks/run_sh.go | 14 +++++++++- .../tasks/tasks_shared.go | 27 +++++++++++++++++-- .../api-reference/starlark-reference/plan.md | 7 +++++ internal_testsuites/golang/scripts/test.sh | 2 +- .../run_task_sh_task_test.go | 12 +++++++++ 6 files changed, 64 insertions(+), 5 deletions(-) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go index 04f982e1ce..9633e36c51 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go @@ -203,8 +203,13 @@ func (builtin *RunPythonCapabilities) Interpret(_ string, arguments *builtin_arg } } + envVars, interpretationErr := extractEnvVarsIfDefined(arguments) + if err != nil { + return nil, interpretationErr + } + // build a service config from image and files artifacts expansion. - builtin.serviceConfig, err = getServiceConfig(image, filesArtifactExpansion) + builtin.serviceConfig, err = getServiceConfig(image, filesArtifactExpansion, envVars) if err != nil { return nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred creating service config using image '%s'", image) } diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go index 8c7b060651..d73e062c35 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go @@ -58,6 +58,12 @@ func NewRunShService(serviceNetwork service_network.ServiceNetwork, runtimeValue IsOptional: true, ZeroValueProvider: builtin_argument.ZeroValueProvider[*starlark.List], }, + { + Name: EnvVarsArgName, + IsOptional: true, + ZeroValueProvider: builtin_argument.ZeroValueProvider[*starlark.Dict], + Validator: nil, + }, { Name: WaitArgName, IsOptional: true, @@ -90,6 +96,7 @@ func NewRunShService(serviceNetwork service_network.ServiceNetwork, runtimeValue FilesArgName: true, StoreFilesArgName: true, WaitArgName: true, + EnvVarsArgName: true, }, } } @@ -147,8 +154,13 @@ func (builtin *RunShCapabilities) Interpret(_ string, arguments *builtin_argumen } } + envVars, interpretationErr := extractEnvVarsIfDefined(arguments) + if err != nil { + return nil, interpretationErr + } + // build a service config from image and files artifacts expansion. - builtin.serviceConfig, err = getServiceConfig(image, filesArtifactExpansion) + builtin.serviceConfig, err = getServiceConfig(image, filesArtifactExpansion, envVars) if err != nil { return nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred creating service config using image '%s'", image) } diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go index e8addad386..5e546a51f8 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go @@ -31,6 +31,7 @@ const ( StoreFilesArgName = "store" WaitArgName = "wait" FilesArgName = "files" + EnvVarsArgName = "env_vars" newlineChar = "\n" @@ -248,7 +249,11 @@ func resultMapToString(resultMap map[string]starlark.Comparable, builtinNameForL return fmt.Sprintf("Command returned with exit code '%v' and the following output: %v", exitCode, outputStr) } -func getServiceConfig(image string, filesArtifactExpansion *service_directory.FilesArtifactsExpansion) (*service.ServiceConfig, error) { +func getServiceConfig( + image string, + filesArtifactExpansion *service_directory.FilesArtifactsExpansion, + envVars *map[string]string, +) (*service.ServiceConfig, error) { serviceConfig, err := service.CreateServiceConfig( image, nil, @@ -262,7 +267,7 @@ func getServiceConfig(image string, filesArtifactExpansion *service_directory.Fi // command is completed runTailCommandToPreventContainerToStopOnCreating, nil, - nil, + *envVars, filesArtifactExpansion, nil, 0, @@ -293,3 +298,21 @@ func removeService(ctx context.Context, serviceNetwork service_network.ServiceNe } return nil } + +func extractEnvVarsIfDefined(arguments *builtin_argument.ArgumentValuesSet) (*map[string]string, *startosis_errors.InterpretationError) { + envVars := map[string]string{} + if arguments.IsSet(EnvVarsArgName) { + envVarsStarlark, err := builtin_argument.ExtractArgumentValue[*starlark.Dict](arguments, EnvVarsArgName) + if err != nil { + return nil, startosis_errors.WrapWithInterpretationError(err, "Unable to extract value for '%s' argument", EnvVarsArgName) + } + if envVarsStarlark != nil && envVarsStarlark.Len() > 0 { + var interpretationErr *startosis_errors.InterpretationError + envVars, interpretationErr = kurtosis_types.SafeCastToMapStringString(envVarsStarlark, EnvVarsArgName) + if interpretationErr != nil { + return nil, interpretationErr + } + } + } + return &envVars, nil +} diff --git a/docs/docs/api-reference/starlark-reference/plan.md b/docs/docs/api-reference/starlark-reference/plan.md index ee8ec2f629..34d18ee71e 100644 --- a/docs/docs/api-reference/starlark-reference/plan.md +++ b/docs/docs/api-reference/starlark-reference/plan.md @@ -477,6 +477,13 @@ The `run_sh` instruction executes a one-time execution task. It runs the bash co # OPTIONAL (Default: badouralix/curl-jq) image = "badouralix/curl-jq", + # Defines environment variables that should be set inside the Docker container running the task. + # OPTIONAL (Default: {}) + env_vars = { + "VAR_1": "VALUE_1", + "VAR_2": "VALUE_2", + }, + # A mapping of path_on_task_where_contents_will_be_mounted -> files_artifact_id_to_mount # For more information about file artifacts, see below. # CAUTION: duplicate paths to files or directories to be mounted is not supported, and it will fail diff --git a/internal_testsuites/golang/scripts/test.sh b/internal_testsuites/golang/scripts/test.sh index 485b4c6592..66e2b6d813 100755 --- a/internal_testsuites/golang/scripts/test.sh +++ b/internal_testsuites/golang/scripts/test.sh @@ -12,7 +12,7 @@ lang_root_dirpath="$(dirname "${script_dirpath}")" # ================================================================================================== PARALLELISM=2 DOCKER_TIMEOUT="3m" # This must be Go-parseable timeout -KUBERNETES_TIMEOUT="8m" # K8S takes longer than docker +KUBERNETES_TIMEOUT="510s" # K8S takes longer than docker TESTSUITE_CLUSTER_BACKEND_DOCKER="docker" TESTSUITE_CLUSTER_BACKEND_KUBERNETES="kubernetes" diff --git a/internal_testsuites/golang/testsuite/startosis_run_sh_task_test/run_task_sh_task_test.go b/internal_testsuites/golang/testsuite/startosis_run_sh_task_test/run_task_sh_task_test.go index 323dcfa25d..6b12a8d4ef 100644 --- a/internal_testsuites/golang/testsuite/startosis_run_sh_task_test/run_task_sh_task_test.go +++ b/internal_testsuites/golang/testsuite/startosis_run_sh_task_test/run_task_sh_task_test.go @@ -55,6 +55,12 @@ def run(plan): result2 = plan.run_sh(run="cat /temp/tech.txt | tr -d '\n'", files={"/temp": file_artifacts[0]}) plan.verify(result2.output, "==", "kurtosis") ` + + runShWithEnvVar = ` +def run(plan): + result = plan.run_sh(run="mkdir -p kurtosis && echo $EXAMPLE",image="badouralix/curl-jq",env_vars={"EXAMPLE": "value"}) + plan.verify(result.output, "==", "value\n") +` ) func TestStarlark_RunshTaskSimple(t *testing.T) { @@ -101,3 +107,9 @@ func TestStarlark_RunShWithNewLineRemovalPipe(t *testing.T) { _, err := test_helpers.SetupSimpleEnclaveAndRunScript(t, ctx, runshTest, runShWithNewLineRemoval) require.Nil(t, err) } + +func TestStarlark_RunShWithEnvVars(t *testing.T) { + ctx := context.Background() + _, err := test_helpers.SetupSimpleEnclaveAndRunScript(t, ctx, runshTest, runShWithEnvVar) + require.Nil(t, err) +} From 8cb7b0b1378c6735894080eb276eb90b2e2d5ca4 Mon Sep 17 00:00:00 2001 From: Anders Schwartz Date: Mon, 5 Feb 2024 16:47:49 -0500 Subject: [PATCH 025/102] docs: add tip for parsing JSON formatted outputs from `command`s (#2120) ## Description: add tip for parsing JSON formatted outputs from `command`s ## Is this change user facing? YES ## References (if applicable): Closes: #2088 --- .../starlark-reference/exec-recipe.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/docs/api-reference/starlark-reference/exec-recipe.md b/docs/docs/api-reference/starlark-reference/exec-recipe.md index eceb143fcd..580643695f 100644 --- a/docs/docs/api-reference/starlark-reference/exec-recipe.md +++ b/docs/docs/api-reference/starlark-reference/exec-recipe.md @@ -34,6 +34,47 @@ If you are trying to run a complex `command` with `|`, you should prefix the com be rewritten as `command = ["/bin/sh", "-c", "echo a | grep a"]`. Not doing so makes everything after the `echo` as args of that command, instead of following the behavior you would expect from a shell. ::: +:::tip +If the executed command returns a proper `JSON` formatted data structure, it's necessary to pass the output through `jq`'s `fromjson` function to enable `jq` to parse the input. +For more information on `jq`'s built-in methods, plese refer to `jq`'s documentation. The following is an example of how to parse the json formatted output using `jq` syntax: + +Example: +``` +def run(plan, args={}): + plan.add_service( + name = "service", + config = ServiceConfig( + image = "alpine", + entrypoint = ["/bin/sh", "-c", "sleep infinity"], + ) + ) + cmd = ''' echo '{"key": "value"}' ''' + result = plan.exec( + service_name = "service", + recipe = ExecRecipe( + command = ["/bin/sh", "-c", cmd], + extract = { + "example_reference_key": "fromjson | .key" # <----- Notice the use of `fromjson` + } + ), + ) + plan.print(result["output"]) + plan.print(result["extract.example_reference_key"]) +``` + +will output: +``` +> print msg="{{kurtosis:1f60460f3eee4036af01b41fc2ecddc0:output.runtime_value}}" +{"key": "value"} + + +> print msg="{{kurtosis:1f60460f3eee4036af01b41fc2ecddc0:extract.example_reference_key.runtime_value}}" +value +``` + +::: + + [exec-reference]: ./plan.md#exec [wait-reference]: ./plan.md#wait From 874767d1b472e88f259741b8b03692fc1c36748f Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Mon, 5 Feb 2024 19:40:48 -0300 Subject: [PATCH 026/102] chore(main): release 0.86.13 (#2094) :robot: I have created a release *beep* *boop* --- ## [0.86.13](https://github.com/kurtosis-tech/kurtosis/compare/0.86.12...0.86.13) (2024-02-05) ### Features * add `env_vars` as input to `run_sh` ([#2114](https://github.com/kurtosis-tech/kurtosis/issues/2114)) ([5a30ea7](https://github.com/kurtosis-tech/kurtosis/commit/5a30ea75865bfc5fb9359d3da206124d13ebc45e)), closes [#2050](https://github.com/kurtosis-tech/kurtosis/issues/2050) * add nodejs devtools to Nix ([#2099](https://github.com/kurtosis-tech/kurtosis/issues/2099)) ([7bbb2bc](https://github.com/kurtosis-tech/kurtosis/commit/7bbb2bc7a5487b05f91089634beed5e83e3329de)) * add run docker compose with kurtosis guide ([#2085](https://github.com/kurtosis-tech/kurtosis/issues/2085)) ([7bbe479](https://github.com/kurtosis-tech/kurtosis/commit/7bbe4796a5b1e952fb85d7cf89136b34ad35f70a)) * Add RunStarlarkScript to enclave manager API ([#2103](https://github.com/kurtosis-tech/kurtosis/issues/2103)) ([1eeb3eb](https://github.com/kurtosis-tech/kurtosis/commit/1eeb3eb3880e8df83c78642dce391070cda9f515)) ### Bug Fixes * adding the `core script build call`, which was removed by accident, in the main build script ([#2118](https://github.com/kurtosis-tech/kurtosis/issues/2118)) ([1f73821](https://github.com/kurtosis-tech/kurtosis/commit/1f738216851718bda1ebf9f9bf8936a715ae2cdf)) * Fix calls to stacktrace in the reverse proxy module ([#2100](https://github.com/kurtosis-tech/kurtosis/issues/2100)) ([a7fefc2](https://github.com/kurtosis-tech/kurtosis/commit/a7fefc235676335d195c4f98a10bb31c5b46a794)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot Co-authored-by: Anders Schwartz --- CHANGELOG.md | 16 ++++++++++++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- .../src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- .../web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 27 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5226b20ed4..06374d363c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## [0.86.13](https://github.com/kurtosis-tech/kurtosis/compare/0.86.12...0.86.13) (2024-02-05) + + +### Features + +* add `env_vars` as input to `run_sh` ([#2114](https://github.com/kurtosis-tech/kurtosis/issues/2114)) ([5a30ea7](https://github.com/kurtosis-tech/kurtosis/commit/5a30ea75865bfc5fb9359d3da206124d13ebc45e)), closes [#2050](https://github.com/kurtosis-tech/kurtosis/issues/2050) +* add nodejs devtools to Nix ([#2099](https://github.com/kurtosis-tech/kurtosis/issues/2099)) ([7bbb2bc](https://github.com/kurtosis-tech/kurtosis/commit/7bbb2bc7a5487b05f91089634beed5e83e3329de)) +* add run docker compose with kurtosis guide ([#2085](https://github.com/kurtosis-tech/kurtosis/issues/2085)) ([7bbe479](https://github.com/kurtosis-tech/kurtosis/commit/7bbe4796a5b1e952fb85d7cf89136b34ad35f70a)) +* Add RunStarlarkScript to enclave manager API ([#2103](https://github.com/kurtosis-tech/kurtosis/issues/2103)) ([1eeb3eb](https://github.com/kurtosis-tech/kurtosis/commit/1eeb3eb3880e8df83c78642dce391070cda9f515)) + + +### Bug Fixes + +* adding the `core script build call`, which was removed by accident, in the main build script ([#2118](https://github.com/kurtosis-tech/kurtosis/issues/2118)) ([1f73821](https://github.com/kurtosis-tech/kurtosis/commit/1f738216851718bda1ebf9f9bf8936a715ae2cdf)) +* Fix calls to stacktrace in the reverse proxy module ([#2100](https://github.com/kurtosis-tech/kurtosis/issues/2100)) ([a7fefc2](https://github.com/kurtosis-tech/kurtosis/commit/a7fefc235676335d195c4f98a10bb31c5b46a794)) + ## [0.86.12](https://github.com/kurtosis-tech/kurtosis/compare/0.86.11...0.86.12) (2024-01-25) diff --git a/LICENSE.md b/LICENSE.md index c12661f723..ca395f33bb 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.12 +Licensed Work: Kurtosis 0.86.13 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-01-25 +Change Date: 2028-02-05 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 5a7448da50..a72190a1cb 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.12" + KurtosisVersion = "0.86.13" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index d54b2dafdf..0ecda66baf 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.12" +version = "0.86.13" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index bd02128164..7b54a1e65a 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.12", + "version": "0.86.13", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index e581e78d94..14d426b57e 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.12" +export const KURTOSIS_VERSION: string = "0.86.13" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 14f40dc221..417adbb596 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.12", + "version": "0.86.13", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index 3768c18526..84ba4e87cb 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.12", + "version": "0.86.13", "private": true, "homepage": ".", "dependencies": { @@ -9,7 +9,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.12", + "kurtosis-ui-components": "0.86.13", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "yaml": "^2.3.4" diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index de3188fc42..bb661e13c3 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.12", + "version": "0.86.13", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 923806c10c..d5189295b8 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.12 +0.86.13 From 4545b79083888e253a4f88b66823ee1d7d6b382e Mon Sep 17 00:00:00 2001 From: Galen Marchetti Date: Mon, 5 Feb 2024 18:37:42 -0600 Subject: [PATCH 027/102] docs: removing the web3 use case links from docs (#2122) ## Description: removing the web3 use case links from the docs ## Is this change user facing? YES --- docs/docusaurus.config.js | 5 ----- docs/sidebars.js | 5 ----- 2 files changed, 10 deletions(-) diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 86a124893d..e84c675db0 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -87,11 +87,6 @@ const config = { label: 'Starlark', activeBasePath: '/sdk' }, - { - href: 'https://web3.kurtosis.com', - position: 'left', - label: 'Kurtosis for Web3', - }, { href: 'https://www.kurtosis.com/release-notes', position: 'left', diff --git a/docs/sidebars.js b/docs/sidebars.js index e500cc602d..3b8dad028c 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -70,11 +70,6 @@ const sidebars = { ] }, 'code-examples', - { - "type": "link", - "label": "Kurtosis for Web3", - "href": "https://web3.kurtosis.com", - }, 'faq', 'best-practices', 'roadmap', From 082720e5a6cf623c51bad9ca95ed4843d87026e9 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Tue, 6 Feb 2024 14:59:53 +0000 Subject: [PATCH 028/102] docs: add docker desktop (#1997) ## Description: ## Is this change user facing? YES/NO ## References (if applicable): --- docs/docs/guides/running-in-k8s.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/guides/running-in-k8s.md b/docs/docs/guides/running-in-k8s.md index a7026a6053..3b8cf08e9a 100644 --- a/docs/docs/guides/running-in-k8s.md +++ b/docs/docs/guides/running-in-k8s.md @@ -73,6 +73,7 @@ We support storage classes that support dynamic provisioning; here are some of t 2. For DigitalOcean we recommend [`do-block-storage`](https://github.com/digitalocean/csi-digitalocean/?tab=readme-ov-file#installing-to-kubernetes) but your cluster should have this out of the box 3. K3s the default provisioner `local-path` should just work out of the box 4. For minikube the default provisioner `standard` should just work out of the box +5. On Docker Desktop Kubernetes the default provisioner is `hostpath` For any other cloud setup please reach out to us by creating an issue on our [GitHub](https://github.com/kurtosis-tech/kurtosis) From eb5bcb9053468b054e28efbc40e4ee459caf203e Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Tue, 6 Feb 2024 17:21:42 +0000 Subject: [PATCH 029/102] fix: allow persistent directory to be reused across services (#2123) There are workflows where people want to use prepopulated persistent volumes in different services. #2121 is an example of this workflow This PR - removes the Service UUID label from the PVCs / Persistent Volumes - Allows PV to be re-used Note in Kubernetes this will break for multi node clusters and this reuse of PVs will have to be in conjunction with node affinity selectors --------- Co-authored-by: Tedi Mitiku --- .../persistent_directories.go | 2 +- .../enclave_object_attributes_provider.go | 13 ++----------- .../persistent_directories.go | 4 +--- .../user_services_functions/start_user_services.go | 1 - .../enclave_object_attributes_provider.go | 4 +--- .../add_service/add_service_shared.go | 3 --- .../startosis_validator/validator_environment.go | 8 -------- 7 files changed, 5 insertions(+), 30 deletions(-) diff --git a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/persistent_directories.go b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/persistent_directories.go index cf52d7a4eb..36c260bc3a 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/persistent_directories.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/user_services_functions/persistent_directories.go @@ -22,7 +22,7 @@ func getOrCreatePersistentDirectories( persistentDirectories := map[string]string{} for serviceDirPath, persistentDirectory := range serviceMountpointsToPersistentKey { - volumeAttrs, err := objAttrsProvider.ForSinglePersistentDirectoryVolume(serviceUuid, persistentDirectory.PersistentKey) + volumeAttrs, err := objAttrsProvider.ForSinglePersistentDirectoryVolume(persistentDirectory.PersistentKey) if err != nil { return nil, stacktrace.Propagate(err, "Error creating persistent directory labels for '%s'", persistentDirectory.PersistentKey) } diff --git a/container-engine-lib/lib/backend_impls/docker/object_attributes_provider/enclave_object_attributes_provider.go b/container-engine-lib/lib/backend_impls/docker/object_attributes_provider/enclave_object_attributes_provider.go index 0271988cf2..96993d5a56 100644 --- a/container-engine-lib/lib/backend_impls/docker/object_attributes_provider/enclave_object_attributes_provider.go +++ b/container-engine-lib/lib/backend_impls/docker/object_attributes_provider/enclave_object_attributes_provider.go @@ -54,7 +54,6 @@ type DockerEnclaveObjectAttributesProvider interface { serviceUUID service.ServiceUUID, ) (DockerObjectAttributes, error) ForSinglePersistentDirectoryVolume( - serviceUUID service.ServiceUUID, persistentKey service_directory.DirectoryPersistentKey, ) (DockerObjectAttributes, error) ForLogsCollector(tcpPortId string, tcpPortSpec *port_spec.PortSpec, httpPortId string, httpPortSpec *port_spec.PortSpec) (DockerObjectAttributes, error) @@ -317,24 +316,21 @@ func (provider *dockerEnclaveObjectAttributesProviderImpl) ForSingleFilesArtifac // In Docker we get one volume per persistent directory func (provider *dockerEnclaveObjectAttributesProviderImpl) ForSinglePersistentDirectoryVolume( - serviceUUID service.ServiceUUID, persistentKey service_directory.DirectoryPersistentKey, ) ( DockerObjectAttributes, error, ) { - serviceUuidStr := string(serviceUUID) - guidStr, err := uuid_generator.GenerateUUIDString() if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred generating a UUID for the persistent directory volume for service '%v'", serviceUuidStr) + return nil, stacktrace.Propagate(err, "An error occurred generating a UUID for the persistent directory volume for persistentKey '%v'", persistentKey) } name, err := provider.getNameForEnclaveObject([]string{ string(persistentKey), }) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred creating the files artifact expansion volume name object using GUID '%v' and service GUID '%v'", guidStr, serviceUuidStr) + return nil, stacktrace.Propagate(err, "An error occurred creating the persistent volume name object using GUID '%v' and persistent key '%v'", guidStr, persistentKey) } labels, err := provider.getLabelsForEnclaveObjectWithGUID(guidStr) @@ -342,11 +338,6 @@ func (provider *dockerEnclaveObjectAttributesProviderImpl) ForSinglePersistentDi return nil, stacktrace.Propagate(err, "An error occurred getting labels for files artifact expansion volume with UUID '%v'", guidStr) } - serviceUuidLabelValue, err := docker_label_value.CreateNewDockerLabelValue(serviceUuidStr) - if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred creating a Docker label value from service GUID string '%v'", serviceUuidStr) - } - labels[docker_label_key.UserServiceGUIDDockerLabelKey] = serviceUuidLabelValue labels[docker_label_key.VolumeTypeDockerLabelKey] = label_value_consts.PersistentDirectoryVolumeTypeDockerLabelValue // TODO Create a KurtosisResourceDockerLabelKey object, like Kubernetes, and apply the "user-service" label here? diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/persistent_directories.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/persistent_directories.go index 69b4409675..9b34812250 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/persistent_directories.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/persistent_directories.go @@ -4,7 +4,6 @@ import ( "context" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/object_attributes_provider" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" "github.com/kurtosis-tech/stacktrace" "github.com/sirupsen/logrus" @@ -69,7 +68,6 @@ func (volumeAndClaim *kubernetesVolumeWithClaim) GetVolumeMount(mountPath string func preparePersistentDirectoriesResources( ctx context.Context, namespace string, - serviceUuid service.ServiceUUID, objAttributeProviders object_attributes_provider.KubernetesEnclaveObjectAttributesProvider, serviceMountpointsToPersistentKey map[string]service_directory.PersistentDirectory, kubernetesManager *kubernetes_manager.KubernetesManager, @@ -80,7 +78,7 @@ func preparePersistentDirectoriesResources( persistentVolumesAndClaims := map[string]*kubernetesVolumeWithClaim{} for dirPath, persistentDirectory := range serviceMountpointsToPersistentKey { - volumeAttrs, err := objAttributeProviders.ForSinglePersistentDirectoryVolume(serviceUuid, persistentDirectory.PersistentKey) + volumeAttrs, err := objAttributeProviders.ForSinglePersistentDirectoryVolume(persistentDirectory.PersistentKey) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred creating the labels for persist service directory '%s'", persistentDirectory.PersistentKey) } diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go index 577bd3f8c2..7fdfd4ea44 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go @@ -356,7 +356,6 @@ func createStartServiceOperation( createVolumesWithClaims, err = preparePersistentDirectoriesResources( ctx, namespaceName, - serviceUuid, enclaveObjAttributesProvider, persistentDirectories.ServiceDirpathToPersistentDirectory, kubernetesManager) diff --git a/container-engine-lib/lib/backend_impls/kubernetes/object_attributes_provider/enclave_object_attributes_provider.go b/container-engine-lib/lib/backend_impls/kubernetes/object_attributes_provider/enclave_object_attributes_provider.go index 88c2c4493d..02d6c914a4 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/object_attributes_provider/enclave_object_attributes_provider.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/object_attributes_provider/enclave_object_attributes_provider.go @@ -43,7 +43,6 @@ type KubernetesEnclaveObjectAttributesProvider interface { userLabels map[string]string, ) (KubernetesObjectAttributes, error) ForSinglePersistentDirectoryVolume( - serviceUUID service.ServiceUUID, persistentKey service_directory.DirectoryPersistentKey, ) (KubernetesObjectAttributes, error) ForUserServiceIngress( @@ -242,10 +241,9 @@ func (provider *kubernetesEnclaveObjectAttributesProviderImpl) ForEnclaveDataDir return objectAttributes, nil } -func (provider *kubernetesEnclaveObjectAttributesProviderImpl) ForSinglePersistentDirectoryVolume(serviceUUID service.ServiceUUID, persistentKey service_directory.DirectoryPersistentKey) (KubernetesObjectAttributes, error) { +func (provider *kubernetesEnclaveObjectAttributesProviderImpl) ForSinglePersistentDirectoryVolume(persistentKey service_directory.DirectoryPersistentKey) (KubernetesObjectAttributes, error) { hasher := md5.New() hasher.Write([]byte(provider.enclaveId)) - hasher.Write([]byte(serviceUUID)) hasher.Write([]byte(persistentKey)) persistentKeyHash := hex.EncodeToString(hasher.Sum(nil)) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go index da32109423..e75fbd10fd 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go @@ -76,9 +76,6 @@ func validateSingleService(validatorEnvironment *startosis_validator.ValidatorEn if !service_directory.IsPersistentKeyValid(directory.PersistentKey) { return startosis_errors.NewValidationError(invalidPersistentKeyErrorText(directory.PersistentKey)) } - if validatorEnvironment.DoesPersistentKeyExist(directory.PersistentKey) == startosis_validator.ComponentCreatedOrUpdatedDuringPackageRun { - return startosis_errors.NewValidationError("There was an error validating '%s' as persistent key '%s' already exists inside the enclave", serviceName, directory.PersistentKey) - } validatorEnvironment.AddPersistentKey(directory.PersistentKey) } } diff --git a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go index bb5b05c05b..b125a0a358 100644 --- a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go +++ b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go @@ -173,11 +173,3 @@ func (environment *ValidatorEnvironment) HasEnoughMemory(memoryToConsume uint64, func (environment *ValidatorEnvironment) AddPersistentKey(persistentKey service_directory.DirectoryPersistentKey) { environment.persistentKeys[persistentKey] = ComponentCreatedOrUpdatedDuringPackageRun } - -func (environment *ValidatorEnvironment) DoesPersistentKeyExist(persistentKey service_directory.DirectoryPersistentKey) ComponentExistence { - persistentKeyExistence, found := environment.persistentKeys[persistentKey] - if !found { - return ComponentNotFound - } - return persistentKeyExistence -} From 674b1ec36df034279d8df95b830d8aa113aace79 Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:14:59 -0300 Subject: [PATCH 030/102] chore(main): release 0.86.14 (#2124) :robot: I have created a release *beep* *boop* --- ## [0.86.14](https://github.com/kurtosis-tech/kurtosis/compare/0.86.13...0.86.14) (2024-02-06) ### Bug Fixes * allow persistent directory to be reused across services ([#2123](https://github.com/kurtosis-tech/kurtosis/issues/2123)) ([eb5bcb9](https://github.com/kurtosis-tech/kurtosis/commit/eb5bcb9053468b054e28efbc40e4ee459caf203e)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 7 +++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- api/typescript/src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 18 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06374d363c..ced0748295 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.86.14](https://github.com/kurtosis-tech/kurtosis/compare/0.86.13...0.86.14) (2024-02-06) + + +### Bug Fixes + +* allow persistent directory to be reused across services ([#2123](https://github.com/kurtosis-tech/kurtosis/issues/2123)) ([eb5bcb9](https://github.com/kurtosis-tech/kurtosis/commit/eb5bcb9053468b054e28efbc40e4ee459caf203e)) + ## [0.86.13](https://github.com/kurtosis-tech/kurtosis/compare/0.86.12...0.86.13) (2024-02-05) diff --git a/LICENSE.md b/LICENSE.md index ca395f33bb..ca47ad8f4e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.13 +Licensed Work: Kurtosis 0.86.14 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-02-05 +Change Date: 2028-02-06 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index a72190a1cb..547be4dc6b 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.13" + KurtosisVersion = "0.86.14" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 0ecda66baf..266796ed0f 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.13" +version = "0.86.14" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index 7b54a1e65a..8d4cd51870 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.13", + "version": "0.86.14", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index 14d426b57e..17585dfeb6 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.13" +export const KURTOSIS_VERSION: string = "0.86.14" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 417adbb596..f445c92376 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.13", + "version": "0.86.14", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index 84ba4e87cb..44e657832d 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.13", + "version": "0.86.14", "private": true, "homepage": ".", "dependencies": { @@ -9,7 +9,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.13", + "kurtosis-ui-components": "0.86.14", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "yaml": "^2.3.4" diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index bb661e13c3..954a25634d 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.13", + "version": "0.86.14", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index d5189295b8..fb98da8728 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.13 +0.86.14 From d33de7bebb0e997c8b2fd93872a1b676dc3519e7 Mon Sep 17 00:00:00 2001 From: Laurent Luce Date: Tue, 6 Feb 2024 16:04:20 -0500 Subject: [PATCH 031/102] feat: Enclave connect (#2117) ## Description: We want the user to be able to connect from their local machine to an enclave with a start from the UI. A connect button is added to the enclave page with the CLI commands to paste. A CLI enclave connect command is added. ## Is this change user facing? YES ## References (if applicable): (https://www.notion.so/kurtosistech/Connect-local-CLI-to-running-enclave-workflow-for-cloud-based-enclaves-via-frontend-636d41ca937c4e49b54161aa498704b1?pvs=4) --- .../core/lib/enclaves/enclave_context.go | 60 ++++++-- .../src/core/lib/enclaves/enclave_context.ts | 2 + .../command_str_consts/command_str_consts.go | 1 + cli/cli/commands/cloud/load/load.go | 14 +- cli/cli/commands/enclave/connect/connect.go | 131 ++++++++++++++++++ cli/cli/commands/enclave/enclave.go | 2 + .../api-reference/engine-apic-reference.md | 11 ++ .../components/modals/ConnectEnclaveModal.tsx | 40 ++++++ .../widgets/ConnectEnclaveButton.tsx | 37 +++++ .../app/src/emui/enclaves/enclave/Enclave.tsx | 5 + 10 files changed, 288 insertions(+), 15 deletions(-) create mode 100644 cli/cli/commands/enclave/connect/connect.go create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx diff --git a/api/golang/core/lib/enclaves/enclave_context.go b/api/golang/core/lib/enclaves/enclave_context.go index f373530b4c..7fbffc2d41 100644 --- a/api/golang/core/lib/enclaves/enclave_context.go +++ b/api/golang/core/lib/enclaves/enclave_context.go @@ -353,26 +353,35 @@ func (enclaveCtx *EnclaveContext) GetServiceContext(serviceIdentifier string) (* serviceIdentifier) } - serviceCtxPrivatePorts, err := convertApiPortsToServiceContextPorts(serviceInfo.GetPrivatePorts()) + serviceContext, err := enclaveCtx.convertServiceInfoToServiceContext(serviceInfo) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred converting the private ports returned by the API to ports usable by the service context") + return nil, stacktrace.Propagate(err, "An error occurred converting the service info to a service context.") } - serviceCtxPublicPorts, err := convertApiPortsToServiceContextPorts(serviceInfo.GetMaybePublicPorts()) + + return serviceContext, nil +} + +// Docs available at https://docs.kurtosis.com/sdk#getservicecontexts--mapstring--bool---servicecontext-servicecontext +func (enclaveCtx *EnclaveContext) GetServiceContexts(serviceIdentifiers map[string]bool) (map[services.ServiceName]*services.ServiceContext, error) { + getServiceInfoArgs := binding_constructors.NewGetServicesArgs(serviceIdentifiers) + response, err := enclaveCtx.client.GetServices(context.Background(), getServiceInfoArgs) if err != nil { - return nil, stacktrace.Propagate(err, "An error occurred converting the public ports returned by the API to ports usable by the service context") + return nil, stacktrace.Propagate( + err, + "An error occurred when trying to get info for services '%v'", + serviceIdentifiers) } - serviceContext := services.NewServiceContext( - enclaveCtx.client, - services.ServiceName(serviceIdentifier), - services.ServiceUUID(serviceInfo.ServiceUuid), - serviceInfo.GetPrivateIpAddr(), - serviceCtxPrivatePorts, - serviceInfo.GetMaybePublicIpAddr(), - serviceCtxPublicPorts, - ) + serviceContexts := make(map[services.ServiceName]*services.ServiceContext, len(response.GetServiceInfo())) + for _, serviceInfo := range response.GetServiceInfo() { + serviceContext, err := enclaveCtx.convertServiceInfoToServiceContext(serviceInfo) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred converting the service info to a service context.") + } + serviceContexts[serviceContext.GetServiceName()] = serviceContext + } - return serviceContext, nil + return serviceContexts, nil } // Docs available at https://docs.kurtosis.com/sdk#getservices---mapservicename--serviceuuid-serviceidentifiers @@ -636,6 +645,29 @@ func (enclaveCtx *EnclaveContext) uploadStarlarkPackage(packageId string, packag return nil } +func (enclaveCtx *EnclaveContext) convertServiceInfoToServiceContext(serviceInfo *kurtosis_core_rpc_api_bindings.ServiceInfo) (*services.ServiceContext, error) { + serviceCtxPrivatePorts, err := convertApiPortsToServiceContextPorts(serviceInfo.GetPrivatePorts()) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred converting the private ports returned by the API to ports usable by the service context") + } + serviceCtxPublicPorts, err := convertApiPortsToServiceContextPorts(serviceInfo.GetMaybePublicPorts()) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred converting the public ports returned by the API to ports usable by the service context") + } + + serviceContext := services.NewServiceContext( + enclaveCtx.client, + services.ServiceName(serviceInfo.Name), + services.ServiceUUID(serviceInfo.ServiceUuid), + serviceInfo.GetPrivateIpAddr(), + serviceCtxPrivatePorts, + serviceInfo.GetMaybePublicIpAddr(), + serviceCtxPublicPorts, + ) + + return serviceContext, nil +} + func getKurtosisYaml(packageRootPath string) (*KurtosisYaml, error) { kurtosisYamlFilepath := path.Join(packageRootPath, kurtosisYamlFilename) diff --git a/api/typescript/src/core/lib/enclaves/enclave_context.ts b/api/typescript/src/core/lib/enclaves/enclave_context.ts index 1c35d1666d..dc63a2536b 100644 --- a/api/typescript/src/core/lib/enclaves/enclave_context.ts +++ b/api/typescript/src/core/lib/enclaves/enclave_context.ts @@ -304,6 +304,8 @@ export class EnclaveContext { return ok(serviceContext); } + // TODO: Add getServiceContexts + // Docs available at https://docs.kurtosis.com/sdk#getservices---mapservicename--serviceuuid-serviceidentifiers public async getServices(): Promise, Error>> { const getAllServicesArgMap: Map = new Map() diff --git a/cli/cli/command_str_consts/command_str_consts.go b/cli/cli/command_str_consts/command_str_consts.go index 9874cb4985..2f39bc320d 100644 --- a/cli/cli/command_str_consts/command_str_consts.go +++ b/cli/cli/command_str_consts/command_str_consts.go @@ -34,6 +34,7 @@ const ( EnclaveStopCmdStr = "stop" EnclaveRmCmdStr = "rm" EnclaveDumpCmdStr = "dump" + EnclaveConnectCmdStr = "connect" EngineCmdStr = "engine" EngineLogsCmdStr = "logs" EngineStartCmdStr = "start" diff --git a/cli/cli/commands/cloud/load/load.go b/cli/cli/commands/cloud/load/load.go index c3832b7a7d..af6174e0b8 100644 --- a/cli/cli/commands/cloud/load/load.go +++ b/cli/cli/commands/cloud/load/load.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "fmt" + "github.com/kurtosis-tech/kurtosis/api/golang/engine/lib/cloud" "github.com/kurtosis-tech/kurtosis/cli/cli/command_framework/highlevel/instance_id_arg" "github.com/kurtosis-tech/kurtosis/cli/cli/command_framework/lowlevel" @@ -50,6 +51,18 @@ func run(ctx context.Context, _ *flags.ParsedFlags, args *args.ParsedArgs) error return stacktrace.Propagate(err, "Expected a value for instance id arg '%v' but none was found; "+ "this is a bug in the Kurtosis CLI!", instanceIdentifierArgKey) } + + contextsConfigStore := store.GetContextsConfigStore() + currentContext, err := contextsConfigStore.GetCurrentContext() + if err != nil { + return stacktrace.Propagate(err, "An error occurred while retrieving the current context") + } + + if currentContext.Uuid.Value == instanceID { + logrus.Infof("Cloud instance %s already loaded", instanceID) + return nil + } + logrus.Infof("Loading cloud instance %s", instanceID) apiKey, err := cloudhelper.LoadApiKey() @@ -96,7 +109,6 @@ func run(ctx context.Context, _ *flags.ParsedFlags, args *args.ParsedArgs) error return stacktrace.Propagate(err, "Unable to decode context config") } - contextsConfigStore := store.GetContextsConfigStore() // We first have to remove the context incase it's already loaded err = contextsConfigStore.RemoveContext(parsedContext.Uuid) if err != nil { diff --git a/cli/cli/commands/enclave/connect/connect.go b/cli/cli/commands/enclave/connect/connect.go new file mode 100644 index 0000000000..9726153f48 --- /dev/null +++ b/cli/cli/commands/enclave/connect/connect.go @@ -0,0 +1,131 @@ +package connect + +import ( + "context" + "fmt" + "strings" + + "github.com/kurtosis-tech/kurtosis/api/golang/core/lib/services" + "github.com/kurtosis-tech/kurtosis/api/golang/engine/kurtosis_engine_rpc_api_bindings" + "github.com/kurtosis-tech/kurtosis/api/golang/engine/lib/kurtosis_context" + "github.com/kurtosis-tech/kurtosis/cli/cli/command_framework/highlevel/enclave_id_arg" + "github.com/kurtosis-tech/kurtosis/cli/cli/command_framework/highlevel/engine_consuming_kurtosis_command" + "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/cli/cli/helpers/portal_manager" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface" + "github.com/kurtosis-tech/kurtosis/contexts-config-store/store" + "github.com/kurtosis-tech/kurtosis/metrics-library/golang/lib/metrics_client" + "github.com/kurtosis-tech/stacktrace" + "github.com/sirupsen/logrus" +) + +const ( + enclaveIdentifierArgKey = "enclave" + isEnclaveIdArgOptional = false + isEnclaveIdArgGreedy = false + + kurtosisBackendCtxKey = "kurtosis-backend" + engineClientCtxKey = "engine-client" + + portMappingSeparatorForLogs = ", " +) + +var EnclaveConnectCmd = &engine_consuming_kurtosis_command.EngineConsumingKurtosisCommand{ + CommandStr: command_str_consts.EnclaveConnectCmdStr, + ShortDescription: "Connects enclave", + LongDescription: "Connects the enclave with the given name", + KurtosisBackendContextKey: kurtosisBackendCtxKey, + EngineClientContextKey: engineClientCtxKey, + Flags: nil, + Args: []*args.ArgConfig{ + enclave_id_arg.NewEnclaveIdentifierArg( + enclaveIdentifierArgKey, + engineClientCtxKey, + isEnclaveIdArgOptional, + isEnclaveIdArgGreedy, + ), + }, + RunFunc: run, +} + +func init() { +} + +func run( + ctx context.Context, + kurtosisBackend backend_interface.KurtosisBackend, + engineClient kurtosis_engine_rpc_api_bindings.EngineServiceClient, + _ metrics_client.MetricsClient, + _ *flags.ParsedFlags, + args *args.ParsedArgs, +) error { + enclaveIdentifier, err := args.GetNonGreedyArg(enclaveIdentifierArgKey) + if err != nil { + return stacktrace.Propagate(err, "An error occurred getting the enclave identifier arg using key '%v'", enclaveIdentifierArgKey) + } + + currentContext, err := store.GetContextsConfigStore().GetCurrentContext() + if err != nil { + logrus.Warnf("Could not retrieve the current context. Kurtosis will assume context is local.") + logrus.Debugf("Error was: %v", err.Error()) + return nil + } + + if !store.IsRemote(currentContext) { + logrus.Info("Current context is local, not mapping enclave service ports") + return nil + } + + logrus.Info("Connecting enclave...") + if err = connectAllEnclaveServices(ctx, enclaveIdentifier); err != nil { + return stacktrace.Propagate(err, "An error occurred connecting all enclave services") + } + + logrus.Info("Enclave connected successfully") + + return nil +} + +func connectAllEnclaveServices(ctx context.Context, enclaveIdentifier string) error { + + kurtosisCtx, err := kurtosis_context.NewKurtosisContextFromLocalEngine() + if err != nil { + return stacktrace.Propagate(err, "An error occurred creating Kurtosis Context from local engine") + } + + enclaveCtx, err := kurtosisCtx.GetEnclaveContext(ctx, enclaveIdentifier) + if err != nil { + return stacktrace.Propagate(err, "An error occurred getting an enclave context from enclave info for enclave '%v'", enclaveIdentifier) + } + allServicesMap := map[string]bool{} + serviceContexts, err := enclaveCtx.GetServiceContexts(allServicesMap) + if err != nil { + return stacktrace.Propagate(err, "An error occurred retrieving the service contexts") + } + + portsMapping := map[uint16]*services.PortSpec{} + for _, serviceCtx := range serviceContexts { + for _, portSpec := range serviceCtx.GetPublicPorts() { + portsMapping[portSpec.GetNumber()] = portSpec + } + } + + portalManager := portal_manager.NewPortalManager() + successfullyMappedPorts, failedPorts, err := portalManager.MapPorts(ctx, portsMapping) + if err != nil { + var stringifiedPortMapping []string + for localPort, remotePort := range failedPorts { + stringifiedPortMapping = append(stringifiedPortMapping, fmt.Sprintf("%d:%d", localPort, remotePort.GetNumber())) + } + logrus.Warnf("The enclave was successfully run but the following port(s) could not be mapped locally: %s. "+ + "The associated service(s) will not be reachable on the local host", + strings.Join(stringifiedPortMapping, portMappingSeparatorForLogs)) + logrus.Debugf("Error was: %v", err.Error()) + return nil + } + logrus.Infof("Successfully mapped %d ports. All services running inside the enclave are reachable locally on"+ + " their ephemeral port numbers", len(successfullyMappedPorts)) + return nil +} diff --git a/cli/cli/commands/enclave/enclave.go b/cli/cli/commands/enclave/enclave.go index 704f840ecc..98257b699f 100644 --- a/cli/cli/commands/enclave/enclave.go +++ b/cli/cli/commands/enclave/enclave.go @@ -8,6 +8,7 @@ package enclave import ( "github.com/kurtosis-tech/kurtosis/cli/cli/command_str_consts" "github.com/kurtosis-tech/kurtosis/cli/cli/commands/enclave/add" + "github.com/kurtosis-tech/kurtosis/cli/cli/commands/enclave/connect" "github.com/kurtosis-tech/kurtosis/cli/cli/commands/enclave/dump" "github.com/kurtosis-tech/kurtosis/cli/cli/commands/enclave/inspect" "github.com/kurtosis-tech/kurtosis/cli/cli/commands/enclave/ls" @@ -31,4 +32,5 @@ func init() { EnclaveCmd.AddCommand(stop.EnclaveStopCmd.MustGetCobraCommand()) EnclaveCmd.AddCommand(rm.EnclaveRmCmd.MustGetCobraCommand()) EnclaveCmd.AddCommand(dump.EnclaveDumpCmd.MustGetCobraCommand()) + EnclaveCmd.AddCommand(connect.EnclaveConnectCmd.MustGetCobraCommand()) } diff --git a/docs/docs/api-reference/engine-apic-reference.md b/docs/docs/api-reference/engine-apic-reference.md index 06be763643..8aefc34959 100644 --- a/docs/docs/api-reference/engine-apic-reference.md +++ b/docs/docs/api-reference/engine-apic-reference.md @@ -302,6 +302,17 @@ Gets relevant information about a service (identified by the given service [iden The [ServiceContext][servicecontext] representation of a service running in a Docker container. +### `getServiceContexts(Map serviceIdentifiers) -> Map serviceContexts` +Gets relevant information about services (identified by the given [service identifiers][service-identifiers]) that are running in the enclave. + +**Args** + +* `serviceIdentifiers`: The [service identifiers][service-identifiers] to indicate which services to retrieve. + +**Returns** + +* `serviceContexts`: A map of objects containing a mapping of Name -> [ServiceContext][servicecontext] for all the services inside the enclave. + ### `getServices() -> Map serviceIdentifiers` Gets the Name and UUID of the current services in the enclave. diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx new file mode 100644 index 0000000000..db9027850e --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx @@ -0,0 +1,40 @@ +import { + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from "@chakra-ui/react"; +import { FileDisplay } from "kurtosis-ui-components"; +import { EnclaveFullInfo } from "../../types"; + +export type ConnectEnclaveModalProps = { + enclave: EnclaveFullInfo; + instanceUUID: string; + isOpen: boolean; + onClose: () => void; +}; + +export const ConnectEnclaveModal = ({ isOpen, onClose, enclave, instanceUUID }: ConnectEnclaveModalProps) => { + const commands = ` + kurtosis cloud load ${instanceUUID} + kurtosis enclave connect ${enclave.name} + kurtosis enclave inspect ${enclave.name}`; + return ( + + + + Connect to this enclave from the CLI + + + + + + The enclave inspect command shows you the ephemeral port to use to connect to your user service. + + + + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx new file mode 100644 index 0000000000..355f499e72 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx @@ -0,0 +1,37 @@ +import { Button, ButtonProps, Tooltip } from "@chakra-ui/react"; +import { useState } from "react"; +import { FiTrash2 } from "react-icons/fi"; +import { EnclaveFullInfo } from "../../types"; +import { ConnectEnclaveModal } from "../modals/ConnectEnclaveModal"; + +type ConnectEnclaveButtonProps = ButtonProps & { + enclave: EnclaveFullInfo; + instanceUUID: string; +}; + +export const ConnectEnclaveButton = ({ enclave, instanceUUID, ...buttonProps }: ConnectEnclaveButtonProps) => { + const [showModal, setShowModal] = useState(false); + + return ( + <> + + + + setShowModal(false)} + /> + + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/enclave/Enclave.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/enclave/Enclave.tsx index 8b69f373b3..c3887e6e51 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/enclave/Enclave.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/enclave/Enclave.tsx @@ -1,9 +1,11 @@ import { Flex, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; +import Cookies from "js-cookie"; import { useNavigate, useParams } from "react-router-dom"; import { AppPageLayout, HoverLineTabList, KurtosisAlert, PageTitle } from "kurtosis-ui-components"; import { FunctionComponent } from "react"; import { EditEnclaveButton } from "../components/EditEnclaveButton"; +import { ConnectEnclaveButton } from "../components/widgets/ConnectEnclaveButton"; import { DeleteEnclavesButton } from "../components/widgets/DeleteEnclavesButton"; import { useFullEnclave } from "../EnclavesContext"; import { EnclaveFullInfo } from "../types"; @@ -43,6 +45,8 @@ const EnclaveImpl = ({ enclave }: EnclaveImplProps) => { navigator(`/enclave/${enclave.shortenedUuid}/${tab.path}`); }; + const instanceUUID = Cookies.get("_kurtosis_instance_id") || ""; + return ( @@ -54,6 +58,7 @@ const EnclaveImpl = ({ enclave }: EnclaveImplProps) => { + From a11fb4c701e7ed8700c7812559aebb03d5d3846b Mon Sep 17 00:00:00 2001 From: Laurent Luce Date: Tue, 6 Feb 2024 18:58:12 -0500 Subject: [PATCH 032/102] fix: Fix the enclave connect button and modal help string in the EM UI (#2127) ## Description: The enclave connect button was the wrong one in the EM UI. Updated to one corresponding to the action. ## Is this change user facing? YES ## References (if applicable): #2117 --- .../emui/enclaves/components/modals/ConnectEnclaveModal.tsx | 2 +- .../emui/enclaves/components/widgets/ConnectEnclaveButton.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx index db9027850e..6e1c3b8264 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx @@ -32,7 +32,7 @@ export const ConnectEnclaveModal = ({ isOpen, onClose, enclave, instanceUUID }: - The enclave inspect command shows you the ephemeral port to use to connect to your user service. + The enclave inspect command shows you the ephemeral ports to use to connect to your user services. diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx index 355f499e72..295c025d08 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx @@ -1,6 +1,6 @@ import { Button, ButtonProps, Tooltip } from "@chakra-ui/react"; import { useState } from "react"; -import { FiTrash2 } from "react-icons/fi"; +import { FiLink2 } from "react-icons/fi"; import { EnclaveFullInfo } from "../../types"; import { ConnectEnclaveModal } from "../modals/ConnectEnclaveModal"; @@ -17,7 +17,7 @@ export const ConnectEnclaveButton = ({ enclave, instanceUUID, ...buttonProps }: + + setShowBuilderModal(false)} + existingEnclave={enclave} + /> + + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/KurtosisArgumentTypeInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/KurtosisArgumentTypeInput.tsx new file mode 100644 index 0000000000..cc7c8e3fd3 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/KurtosisArgumentTypeInput.tsx @@ -0,0 +1,97 @@ +import { ArgumentValueType } from "kurtosis-cloud-indexer-sdk"; +import { assertDefined } from "kurtosis-ui-components"; +import { BooleanArgumentInput } from "../form/BooleanArgumentInput"; +import { DictArgumentInput } from "../form/DictArgumentInput"; +import { IntegerArgumentInput } from "../form/IntegerArgumentInput"; +import { JSONArgumentInput } from "../form/JSONArgumentInput"; +import { ListArgumentInput } from "../form/ListArgumentInput"; +import { StringArgumentInput } from "../form/StringArgumentInput"; +import { KurtosisFormInputProps } from "../form/types"; +import { ConfigureEnclaveForm } from "./types"; + +type KurtosisArgumentTypeInputProps = KurtosisFormInputProps & { + type?: ArgumentValueType; + subType1?: ArgumentValueType; + subType2?: ArgumentValueType; +}; + +export const KurtosisArgumentTypeInput = ({ + type, + subType1, + subType2, + name, + placeholder, + isRequired, + validate, + disabled, + width, + size, + tabIndex, +}: KurtosisArgumentTypeInputProps) => { + const childProps: KurtosisFormInputProps = { + name, + placeholder, + isRequired, + validate, + disabled, + width, + size, + tabIndex, + }; + + switch (type) { + case ArgumentValueType.INTEGER: + return ; + case ArgumentValueType.DICT: + assertDefined( + subType1, + `innerType1 was not defined on DICT argument ${name}, check the format used matches https://docs.kurtosis.com/api-reference/starlark-reference/docstring-syntax#types`, + ); + assertDefined( + subType2, + `innerType2 was not defined on DICT argument ${name}, check the format used matches https://docs.kurtosis.com/api-reference/starlark-reference/docstring-syntax#types`, + ); + return ( + ( + + )} + ValueFieldComponent={(props) => ( + + )} + {...childProps} + /> + ); + case ArgumentValueType.LIST: + assertDefined( + subType1, + `innerType1 was not defined on DICT argument ${name}, check the format used matches https://docs.kurtosis.com/api-reference/starlark-reference/docstring-syntax#types`, + ); + return ( + ( + + )} + createNewValue={() => ({ value: "" })} + {...childProps} + /> + ); + case ArgumentValueType.BOOL: + return ; + case ArgumentValueType.STRING: + return ; + case ArgumentValueType.JSON: + default: + return ; + } +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/KurtosisPackageArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/KurtosisPackageArgumentInput.tsx index 8694d79ec9..8a5aac7824 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/KurtosisPackageArgumentInput.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/KurtosisPackageArgumentInput.tsx @@ -1,6 +1,6 @@ import { PackageArg } from "kurtosis-cloud-indexer-sdk"; -import { KurtosisArgumentTypeInput } from "./inputs/KurtosisArgumentTypeInput"; -import { KurtosisArgumentFormControl } from "./KurtosisArgumentFormControl"; +import { KurtosisFormControl } from "../form/KurtosisFormControl"; +import { KurtosisArgumentTypeInput } from "./KurtosisArgumentTypeInput"; import { argToTypeString } from "./utils"; type KurtosisPackageArgumentInputProps = { @@ -22,7 +22,7 @@ export const KurtosisPackageArgumentInput = ({ argument, disabled }: KurtosisPac .join(" "); return ( - - + ); }; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/drawer/bodies/EnclaveConfigureBody.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/drawer/bodies/EnclaveConfigureBody.tsx index ba86b69e3f..b8c8ca78bd 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/drawer/bodies/EnclaveConfigureBody.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/drawer/bodies/EnclaveConfigureBody.tsx @@ -38,11 +38,11 @@ import { useNavigate } from "react-router-dom"; import { useKurtosisClient } from "../../../../../../client/enclaveManager/KurtosisClientContext"; import { useEnclavesContext } from "../../../../EnclavesContext"; import { EnclaveFullInfo } from "../../../../types"; +import { BooleanArgumentInput } from "../../../form/BooleanArgumentInput"; +import { KurtosisFormControl } from "../../../form/KurtosisFormControl"; +import { StringArgumentInput } from "../../../form/StringArgumentInput"; import { allowedEnclaveNamePattern, isEnclaveNameAllowed } from "../../../utils"; import { EnclaveConfigurationForm, EnclaveConfigurationFormImperativeAttributes } from "../../EnclaveConfigurationForm"; -import { BooleanArgumentInput } from "../../inputs/BooleanArgumentInput"; -import { StringArgumentInput } from "../../inputs/StringArgumentInput"; -import { KurtosisArgumentFormControl } from "../../KurtosisArgumentFormControl"; import { KurtosisPackageArgumentInput } from "../../KurtosisPackageArgumentInput"; import { ConfigureEnclaveForm } from "../../types"; import { transformKurtosisArgsToFormArgs } from "../../utils"; @@ -272,7 +272,7 @@ export const EnclaveConfigureBody = forwardRef - + - + ({ find: () => searchRef.current?.focus() }), [searchRef])); + useEffect(() => { startCheckSinglePackage(searchTerm); }, [startCheckSinglePackage, searchTerm]); @@ -197,6 +200,16 @@ export const PackageSelectBody = ({ onClick={() => onPackageSelected(kurtosisPackage)} /> ))} + + All Packages + + {searchResults.value.map((kurtosisPackage) => ( + onPackageSelected(kurtosisPackage)} + /> + ))} )} diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/drawer/constants.ts b/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/drawer/constants.ts index 24c20621ee..f4db1f11bc 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/drawer/constants.ts +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/drawer/constants.ts @@ -1,3 +1,4 @@ export const KURTOSIS_PACKAGE_PARAMS_URL_ARG = "package-args"; export const KURTOSIS_PACKAGE_ID_URL_ARG = "package-id"; export const KURTOSIS_CREATE_ENCLAVE_URL_ARG = "create-enclave"; +export const KURTOSIS_BUILD_ENCLAVE_URL_ARG = "build-enclave"; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/inputs/KurtosisArgumentTypeInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/inputs/KurtosisArgumentTypeInput.tsx deleted file mode 100644 index cda736a1de..0000000000 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/inputs/KurtosisArgumentTypeInput.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import * as CSS from "csstype"; -import { ArgumentValueType } from "kurtosis-cloud-indexer-sdk"; -import { assertDefined } from "kurtosis-ui-components"; -import { FieldPath } from "react-hook-form"; -import { ConfigureEnclaveForm } from "../types"; -import { BooleanArgumentInput } from "./BooleanArgumentInput"; -import { DictArgumentInput } from "./DictArgumentInput"; -import { IntegerArgumentInput } from "./IntegerArgumentInput"; -import { JSONArgumentInput } from "./JSONArgumentInput"; -import { ListArgumentInput } from "./ListArgumentInput"; -import { StringArgumentInput } from "./StringArgumentInput"; - -type KurtosisArgumentTypeInputProps = { - type?: ArgumentValueType; - subType1?: ArgumentValueType; - subType2?: ArgumentValueType; - name: FieldPath; - placeholder?: string; - isRequired?: boolean; - validate?: (value: any) => string | undefined; - disabled?: boolean; - width?: CSS.Property.Width; - size?: string; - tabIndex?: number; -}; - -export type KurtosisArgumentTypeInputImplProps = Omit; - -export const KurtosisArgumentTypeInput = ({ - type, - subType1, - subType2, - name, - placeholder, - isRequired, - validate, - disabled, - width, - size, - tabIndex, -}: KurtosisArgumentTypeInputProps) => { - const childProps: KurtosisArgumentTypeInputImplProps = { - name, - placeholder, - isRequired, - validate, - disabled, - width, - size, - tabIndex, - }; - - switch (type) { - case ArgumentValueType.INTEGER: - return ; - case ArgumentValueType.DICT: - assertDefined( - subType1, - `innerType1 was not defined on DICT argument ${name}, check the format used matches https://docs.kurtosis.com/api-reference/starlark-reference/docstring-syntax#types`, - ); - assertDefined( - subType2, - `innerType2 was not defined on DICT argument ${name}, check the format used matches https://docs.kurtosis.com/api-reference/starlark-reference/docstring-syntax#types`, - ); - return ; - case ArgumentValueType.LIST: - assertDefined( - subType1, - `innerType1 was not defined on DICT argument ${name}, check the format used matches https://docs.kurtosis.com/api-reference/starlark-reference/docstring-syntax#types`, - ); - return ; - case ArgumentValueType.BOOL: - return ; - case ArgumentValueType.STRING: - return ; - case ArgumentValueType.JSON: - default: - return ; - } -}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/inputs/BooleanArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/BooleanArgumentInput.tsx similarity index 68% rename from enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/inputs/BooleanArgumentInput.tsx rename to enclave-manager/web/packages/app/src/emui/enclaves/components/form/BooleanArgumentInput.tsx index b28b6ea7ac..aaeddad9b1 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/inputs/BooleanArgumentInput.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/BooleanArgumentInput.tsx @@ -1,13 +1,17 @@ import { Radio, RadioGroup, Stack, Switch } from "@chakra-ui/react"; -import { useEnclaveConfigurationFormContext } from "../EnclaveConfigurationForm"; -import { KurtosisArgumentTypeInputImplProps } from "./KurtosisArgumentTypeInput"; -type BooleanArgumentInputProps = KurtosisArgumentTypeInputImplProps & { +import { useFormContext } from "react-hook-form"; +import { KurtosisFormInputProps } from "./types"; + +type BooleanArgumentInputProps = KurtosisFormInputProps & { inputType?: "radio" | "switch"; }; -export const BooleanArgumentInput = ({ inputType, ...props }: BooleanArgumentInputProps) => { - const { register, getValues } = useEnclaveConfigurationFormContext(); +export const BooleanArgumentInput = ({ + inputType, + ...props +}: BooleanArgumentInputProps) => { + const { register, getValues } = useFormContext(); const currentDefault = getValues(props.name); @@ -17,7 +21,8 @@ export const BooleanArgumentInput = ({ inputType, ...props }: BooleanArgumentInp {...register(props.name, { disabled: props.disabled, required: props.isRequired, - value: true, + // any required to force this initial value to work. + value: true as any, validate: props.validate, })} /> diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/inputs/DictArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/DictArgumentInput.tsx similarity index 62% rename from enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/inputs/DictArgumentInput.tsx rename to enclave-manager/web/packages/app/src/emui/enclaves/components/form/DictArgumentInput.tsx index 27bb67f96d..18deb429ce 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/configuration/inputs/DictArgumentInput.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/DictArgumentInput.tsx @@ -1,30 +1,30 @@ import { Button, ButtonGroup, Flex, useToast } from "@chakra-ui/react"; -import { ArgumentValueType } from "kurtosis-cloud-indexer-sdk"; import { CopyButton, PasteButton, stringifyError } from "kurtosis-ui-components"; +import { FC } from "react"; import { useFieldArray, useFormContext } from "react-hook-form"; import { FiDelete, FiPlus } from "react-icons/fi"; -import { KurtosisArgumentSubtypeFormControl } from "../KurtosisArgumentFormControl"; -import { ConfigureEnclaveForm } from "../types"; -import { KurtosisArgumentTypeInput, KurtosisArgumentTypeInputImplProps } from "./KurtosisArgumentTypeInput"; +import { KurtosisSubtypeFormControl } from "./KurtosisFormControl"; +import { KurtosisFormInputProps } from "./types"; -type DictArgumentInputProps = KurtosisArgumentTypeInputImplProps & { - keyType: ArgumentValueType; - valueType: ArgumentValueType; +type DictArgumentInputProps = KurtosisFormInputProps & { + KeyFieldComponent: FC>; + ValueFieldComponent: FC>; }; -export const DictArgumentInput = ({ keyType, valueType, ...otherProps }: DictArgumentInputProps) => { +export const DictArgumentInput = ({ + KeyFieldComponent, + ValueFieldComponent, + ...otherProps +}: DictArgumentInputProps) => { const toast = useToast(); - const { getValues, setValue } = useFormContext(); + const { getValues, setValue } = useFormContext(); const { fields, append, remove } = useFieldArray({ name: otherProps.name }); const handleValuePaste = (value: string) => { try { const parsed = JSON.parse(value); - setValue( - otherProps.name, - Object.entries(parsed).map(([key, value]) => ({ key, value })), - ); + setValue(otherProps.name, Object.entries(parsed).map(([key, value]) => ({ key, value })) as any); } catch (err: any) { toast({ title: `Could not read pasted input, was it a json object? Got error: ${stringifyError(err)}`, @@ -40,7 +40,7 @@ export const DictArgumentInput = ({ keyType, valueType, ...otherProps }: DictArg contentName={"value"} valueToCopy={() => JSON.stringify( - getValues(otherProps.name).reduce( + (getValues(otherProps.name) as any[]).reduce( (acc: Record, { key, value }: { key: string; value: any }) => ({ ...acc, [key]: value }), {}, ), @@ -51,34 +51,32 @@ export const DictArgumentInput = ({ keyType, valueType, ...otherProps }: DictArg {fields.map((field, i) => ( - - - - + - - + @@ -86,7 +84,7 @@ export const DictArgumentInput = ({ keyType, valueType, ...otherProps }: DictArg ))} @@ -64,7 +55,7 @@ export const ListArgumentInput = ({ valueType, ...otherProps }: ListArgumentInpu ))} + + + + + + ); +}; + +const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); + +const getLayoutedElements = (nodes: Node[], edges: Edge[]) => { + if (nodes.length === 0) { + return { nodes, edges }; + } + g.setGraph({ rankdir: "LR", ranksep: 100 }); + + edges.forEach((edge) => g.setEdge(edge.source, edge.target)); + nodes.forEach((node) => + g.setNode(node.id, node as Node<{ label: string }, string | undefined> & { width?: number; height?: number }), + ); + + Dagre.layout(g); + + return { + nodes: nodes.map((node) => { + const { x, y } = g.node(node.id); + + return { ...node, position: { x, y } }; + }), + edges, + }; +}; + +type VisualiserImperativeAttributes = { + getStarlark: () => string; +}; + +type VisualiserProps = { + initialNodes: Node[]; + initialEdges: Edge[]; + existingEnclave?: RemoveFunctions; +}; + +const Visualiser = forwardRef( + ({ initialNodes, initialEdges, existingEnclave }, ref) => { + const { data, updateData } = useVariableContext(); + const insertOffset = useRef(0); + const { fitView, addNodes, getViewport } = useReactFlow(); + const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes || []); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges || []); + + const nodeTypes = useMemo(() => ({ serviceNode: KurtosisServiceNode, artifactNode: KurtosisArtifactNode }), []); + + const onLayout = useCallback(() => { + const layouted = getLayoutedElements(nodes, edges); + + setNodes([...layouted.nodes]); + setEdges([...layouted.edges]); + + window.requestAnimationFrame(() => { + fitView(); + }); + }, [nodes, edges, fitView, setEdges, setNodes]); + + const getNewNodePosition = (): XYPosition => { + const viewport = getViewport(); + insertOffset.current += 1; + return { x: -viewport.x + insertOffset.current * 20 + 400, y: -viewport.y + insertOffset.current * 20 }; + }; + + const handleAddServiceNode = () => { + const id = uuidv4(); + updateData(id, { type: "service", serviceName: "", image: "", ports: [], env: [], files: [], isValid: false }); + addNodes({ + id, + position: getNewNodePosition(), + width: 600, + type: "serviceNode", + data: {}, + }); + }; + + const handleAddArtifactNode = () => { + const id = uuidv4(); + updateData(id, { type: "artifact", artifactName: "", files: {}, isValid: false }); + addNodes({ + id, + position: getNewNodePosition(), + width: 600, + type: "artifactNode", + data: {}, + }); + }; + + useEffect(() => { + setEdges((prevState) => { + return Object.entries(getNodeDependencies(data)).flatMap(([to, froms]) => + [...froms].map((from) => ({ + id: `${from}-${to}`, + source: from, + target: to, + animated: true, + style: { strokeWidth: "3px" }, + })), + ); + }); + }, [setEdges, data]); + + useImperativeHandle( + ref, + () => ({ + getStarlark: () => { + return generateStarlarkFromGraph(nodes, edges, data, existingEnclave); + }, + }), + [nodes, edges, data, existingEnclave], + ); + + return ( + + + + + + + + (insertOffset.current = 1)} + onNodesChange={onNodesChange} + onEdgesChange={onEdgesChange} + nodeTypes={nodeTypes} + fitView + > + + + + + + ); + }, +); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx new file mode 100644 index 0000000000..58c62bdab6 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx @@ -0,0 +1,109 @@ +import { Flex, IconButton, Text } from "@chakra-ui/react"; +import { memo } from "react"; +import { FormProvider, useForm } from "react-hook-form"; +import { FiTrash } from "react-icons/fi"; +import { RxCornerBottomRight } from "react-icons/rx"; +import { Handle, NodeProps, NodeResizeControl, Position, useReactFlow } from "reactflow"; +import { KurtosisFormControl } from "../../form/KurtosisFormControl"; +import { StringArgumentInput } from "../../form/StringArgumentInput"; +import { FileTreeArgumentInput } from "./input/FileTreeArgumentInput"; +import { KurtosisArtifactNodeData, useVariableContext } from "./VariableContextProvider"; + +export const KurtosisArtifactNode = memo( + ({ id, selected }: NodeProps) => { + const { data, updateData, removeData } = useVariableContext(); + const formMethods = useForm({ + defaultValues: (data[id] as KurtosisArtifactNodeData) || {}, + mode: "onBlur", + shouldFocusError: false, + }); + + const { deleteElements } = useReactFlow(); + + const handleDeleteNode = (e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + deleteElements({ nodes: [{ id }] }); + removeData(id); + }; + + const handleBlur = async () => { + const isValid = await formMethods.trigger(); + updateData(id, { ...formMethods.getValues(), isValid }); + }; + + return ( + + + + + + + + + + + {(data[id] as KurtosisArtifactNodeData)?.artifactName || Unnamed Artifact} + + } + colorScheme={"red"} + variant={"ghost"} + size={"sm"} + onClick={handleDeleteNode} + /> + + + name={"artifactName"} label={"Artifact Name"}> + + + + + + + + + ); + }, + (oldProps, newProps) => { + return oldProps.id === newProps.id && oldProps.selected === newProps.selected; + }, +); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx new file mode 100644 index 0000000000..8f164c2325 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx @@ -0,0 +1,241 @@ +import { Flex, Grid, GridItem, IconButton, Text } from "@chakra-ui/react"; +import { memo, useMemo } from "react"; +import { FormProvider, useForm } from "react-hook-form"; +import { FiTrash } from "react-icons/fi"; +import { RxCornerBottomRight } from "react-icons/rx"; +import { Handle, NodeProps, NodeResizeControl, Position, useReactFlow } from "reactflow"; +import { DictArgumentInput } from "../../form/DictArgumentInput"; +import { IntegerArgumentInput } from "../../form/IntegerArgumentInput"; +import { KurtosisFormControl } from "../../form/KurtosisFormControl"; +import { ListArgumentInput } from "../../form/ListArgumentInput"; +import { OptionsArgumentInput } from "../../form/OptionArgumentInput"; +import { SelectArgumentInput, SelectOption } from "../../form/SelectArgumentInput"; +import { StringArgumentInput } from "../../form/StringArgumentInput"; +import { MentionStringArgumentInput } from "./input/MentionStringArgumentInput"; +import { + KurtosisFileMount, + KurtosisPort, + KurtosisServiceNodeData, + useVariableContext, +} from "./VariableContextProvider"; + +export const KurtosisServiceNode = memo( + ({ id, selected }: NodeProps) => { + const { data, updateData, removeData, variables } = useVariableContext(); + const artifactVariableOptions = useMemo((): SelectOption[] => { + return variables + .filter((variable) => variable.id.startsWith("artifact")) + .map((variable) => ({ display: variable.displayName, value: `{{${variable.id}}}` })); + }, [variables]); + const formMethods = useForm({ + defaultValues: (data[id] as KurtosisServiceNodeData) || {}, + mode: "onBlur", + shouldFocusError: false, + }); + + const { deleteElements } = useReactFlow(); + + const handleDeleteNode = (e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + deleteElements({ nodes: [{ id }] }); + removeData(id); + }; + + const handleBlur = async () => { + const isValid = await formMethods.trigger(); + updateData(id, { ...formMethods.getValues(), isValid }); + }; + + return ( + + + + + + + + + + + {(data[id] as KurtosisServiceNodeData)?.serviceName || Unnamed Service} + + } + colorScheme={"red"} + variant={"ghost"} + size={"sm"} + onClick={handleDeleteNode} + /> + + + name={"serviceName"} label={"Service Name"} isRequired> + { + if (typeof val !== "string") { + return "Value should be a string"; + } + if (!val.match(/^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$/)) { + return ( + "Service names must adhere to the RFC 1035 standard, specifically implementing this regex and" + + " be 1-63 characters long: ^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$. This means the service name must " + + "only contain lowercase alphanumeric characters or '-', and must start with a lowercase alphabet " + + "and end with a lowercase alphanumeric" + ); + } + }} + /> + + name={"image"} label={"Container Image"} isRequired> + { + if (typeof val !== "string") { + return "Value should be a string"; + } + if ( + !val.match( + /^(?[\w.\-_]+((?::\d+|)(?=\/[a-z0-9._-]+\/[a-z0-9._-]+))|)(?:\/|)(?[a-z0-9.\-_]+(?:\/[a-z0-9.\-_]+|))(:(?[\w.\-_]{1,127})|)$/gim, + ) + ) { + return "Value does not look like a docker image"; + } + }} + /> + + name={"env"} label={"Environment Variables"}> + + name={"env"} + KeyFieldComponent={StringArgumentInput} + ValueFieldComponent={MentionStringArgumentInput} + /> + + name={"ports"} label={"Ports"}> + ( + + + + {...props} + size={"sm"} + placeholder={"Port Name (eg postgres)"} + name={`${props.name as `ports.${number}`}.portName`} + /> + + + + {...props} + size={"sm"} + placeholder={"Application Protocol (eg postgresql)"} + name={`${props.name as `ports.${number}`}.applicationProtocol`} + /> + + + + {...props} + options={["TCP", "UDP"]} + name={`${props.name as `ports.${number}`}.transportProtocol`} + /> + + + + {...props} + name={`${props.name as `ports.${number}`}.port`} + size={"sm"} + /> + + + )} + createNewValue={(): KurtosisPort => ({ + portName: "", + applicationProtocol: "", + transportProtocol: "TCP", + port: 0, + })} + /> + + + name={"files"} + label={"Files"} + helperText={"Choose where to mount artifacts on this services filesystem"} + > + ( + + + + {...props} + size={"sm"} + placeholder={"/some/path"} + name={`${props.name as `files.${number}`}.mountPoint`} + /> + + + + options={artifactVariableOptions} + {...props} + size={"sm"} + placeholder={"Select an Artifact"} + name={`${props.name as `files.${number}`}.artifactName`} + /> + + + )} + createNewValue={(): KurtosisFileMount => ({ + mountPoint: "", + artifactName: "", + })} + /> + + + + + ); + }, + (oldProps, newProps) => { + return oldProps.id === newProps.id && oldProps.selected === newProps.selected; + }, +); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/VariableContextProvider.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/VariableContextProvider.tsx new file mode 100644 index 0000000000..d5e3d742cc --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/VariableContextProvider.tsx @@ -0,0 +1,78 @@ +import { createContext, PropsWithChildren, useCallback, useContext, useMemo, useState } from "react"; +import { Variable } from "./types"; +import { getVariablesFromNodes } from "./utils"; + +export type KurtosisPort = { + portName: string; + port: number; + transportProtocol: "TCP" | "UDP"; + applicationProtocol: string; +}; + +export type KurtosisFileMount = { + mountPoint: string; + artifactName: string; +}; + +export type KurtosisServiceNodeData = { + type: "service"; + serviceName: string; + image: string; + env: { key: string; value: string }[]; + ports: KurtosisPort[]; + files: KurtosisFileMount[]; + isValid: boolean; +}; + +export type KurtosisArtifactNodeData = { + type: "artifact"; + artifactName: string; + files: Record; + isValid: boolean; +}; + +export type KurtosisNodeData = KurtosisArtifactNodeData | KurtosisServiceNodeData; + +type VariableContextState = { + data: Record; + variables: Variable[]; + updateData: (id: string, data: KurtosisNodeData) => void; + removeData: (id: string) => void; +}; + +const VariableContext = createContext({ + data: {}, + variables: [], + updateData: () => null, + removeData: () => null, +}); + +type VariableContextProviderProps = { + initialData: Record; +}; + +export const VariableContextProvider = ({ initialData, children }: PropsWithChildren) => { + const [data, setData] = useState>(initialData); + + const variables = useMemo((): Variable[] => { + return getVariablesFromNodes(data); + }, [data]); + + const updateData = useCallback((id: string, data: KurtosisNodeData) => { + setData((oldData) => ({ ...oldData, [id]: data })); + }, []); + + const removeData = useCallback((id: string) => { + setData((oldData) => { + const r = { ...oldData }; + delete r[id]; + return r; + }); + }, []); + + return ( + {children} + ); +}; + +export const useVariableContext = () => useContext(VariableContext); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/FileTreeArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/FileTreeArgumentInput.tsx new file mode 100644 index 0000000000..dd9c68a5c4 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/FileTreeArgumentInput.tsx @@ -0,0 +1,126 @@ +import { Button, ButtonGroup, Flex } from "@chakra-ui/react"; +import { FileTree, FileTreeNode, isDefined } from "kurtosis-ui-components"; +import { useMemo, useState } from "react"; +import { Controller } from "react-hook-form"; +import { FiPlus } from "react-icons/fi"; +import { KurtosisFormInputProps } from "../../../form/types"; +import { EditFileModal } from "./modals/EditFileModal"; +import { NewFileModal } from "./modals/NewFileModal"; + +type FileTreeArgumentInputProps = KurtosisFormInputProps; + +export const FileTreeArgumentInput = ({ + name, + isRequired, + validate, + disabled, +}: FileTreeArgumentInputProps) => { + return ( + { + return ; + }} + /> + ); +}; + +type FileTreeInputProps = { + files: Record; + onUpdateFiles: (newFiles: Record) => void; +}; + +const FileTreeInput = ({ files, onUpdateFiles }: FileTreeInputProps) => { + const [selectedPath, setSelectedPath] = useState(); + const [showNewFileInputDialog, setShowNewFileInputDialog] = useState(false); + const [editingFilePath, setEditingFilePath] = useState(); + + const fileTree = useMemo((): FileTreeNode[] => { + return ( + Object.entries(files).reduce( + (acc, [fileName, fileContent]) => { + let filePath = fileName.split("/"); + if (filePath[0] === "") { + filePath = filePath.slice(1); + } + let destinationNode = acc; + let i = 0; + while (i < filePath.length - 1) { + const filePart = filePath[i]; + let nextNode = destinationNode.childNodes?.find((node) => node.name === filePart); + if (!isDefined(nextNode)) { + nextNode = { name: filePart, childNodes: [] }; + destinationNode.childNodes?.push(nextNode); + } + destinationNode = nextNode; + i++; + } + destinationNode.childNodes?.push({ + name: filePath[filePath.length - 1], + size: BigInt(fileContent.length), + }); + + return acc; + }, + { name: "/", childNodes: [] } as FileTreeNode, + ).childNodes || [] + ); + }, [files]); + + const handleNewFile = (newFileName: string) => { + setShowNewFileInputDialog(false); + onUpdateFiles({ ...files, [newFileName]: "" }); + }; + + const handleSaveEditedFile = (text: string) => { + if (!isDefined(editingFilePath)) { + return; + } + onUpdateFiles({ ...files, ["/" + editingFilePath.join("/")]: text }); + setEditingFilePath(undefined); + }; + + const handleDeleteSelectedFile = () => { + if (!isDefined(selectedPath)) { + return; + } + const newFiles = { ...files }; + delete newFiles["/" + selectedPath.join("/")]; + onUpdateFiles(newFiles); + }; + + return ( + + + + + + + setShowNewFileInputDialog(false)} + onConfirm={handleNewFile} + /> + setEditingFilePath(undefined)} + filePath={editingFilePath || []} + file={files["/" + editingFilePath?.join("/")]} + onSave={handleSaveEditedFile} + /> + + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.css b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.css new file mode 100644 index 0000000000..65a156a440 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.css @@ -0,0 +1,63 @@ +.mentions { + appearance: none; + height: var(--input-height); + font-size: var(--input-font-size); + border: 1px solid; + border-color: inherit; + border-radius: var(--input-border-radius); + outline: 2px solid transparent; + outline-offset: 2px; + padding-inline-start: var(--input-padding); + padding-inline-end: var(--input-padding); + --input-font-size: var(--chakra-fontSizes-sm); + --input-padding: var(--chakra-space-3); + --input-border-radius: var(--chakra-radii-sm); + --input-height: var(--chakra-sizes-8); + padding-top: 4px; + + &[aria-invalid="true"] { + border-color: var(--chakra-colors-red-300); + box-shadow: 0 0 0 1px var(--chakra-colors-red-300); + } + + &:focus-within { + z-index: 1; + border-color: #63b3ed; + box-shadow: 0 0 0 1px #63b3ed; + } +} + +.mentions--singleLine .mentions__highlighter { +} + +.mentions--singleLine .mentions__input { + padding-inline-start: var(--input-padding); + padding-inline-end: var(--input-padding); + --input-padding: var(--chakra-space-3); + + padding-top: 4px; + + &:focus-visible { + outline: none; + } +} + +.mentions__suggestions__list { + background-color: var(--chakra-colors-gray-800); + border: 1px solid rgba(0, 0, 0, 0.15); + font-size: 10pt; +} + +.mentions__suggestions__item { + padding: 5px 15px; + border-bottom: 1px solid rgba(0, 0, 0, 0.15); +} + +.mentions__suggestions__item--focused { + background-color: var(--chakra-colors-gray-600); +} + +.mentions__mention { + background-color: var(--chakra-colors-blue-500); + border-radius: 2px; +} diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx new file mode 100644 index 0000000000..7684098336 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx @@ -0,0 +1,71 @@ +import { isDefined } from "kurtosis-ui-components"; +import { useCallback } from "react"; +import { Controller } from "react-hook-form"; +import { Mention, MentionsInput } from "react-mentions"; +import { KurtosisFormInputProps } from "../../../form/types"; +import { useVariableContext } from "../VariableContextProvider"; +import "./MentionStringArgumentInput.css"; + +type MentionStringArgumentInputProps = KurtosisFormInputProps; + +export const MentionStringArgumentInput = ({ + name, + placeholder, + isRequired, + validate, + disabled, + width, + size, + tabIndex, +}: MentionStringArgumentInputProps) => { + const { variables } = useVariableContext(); + + const handleQuery = useCallback( + (query?: string) => { + if (!isDefined(query)) { + return []; + } + const suggestions = variables.map((v) => ({ display: v.displayName, id: v.id })); + const queryTerms = query.toLowerCase().split(/\s+|\./); + return suggestions.filter((variable) => queryTerms.every((term) => variable.display.includes(term))); + }, + [variables], + ); + + return ( + { + return ( + field.onChange(newValue)} + > + + variables.find((variable) => variable.id === id)?.displayName || "Missing Variable" + } + /> + + ); + }} + /> + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/EditFileModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/EditFileModal.tsx new file mode 100644 index 0000000000..65361a7928 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/EditFileModal.tsx @@ -0,0 +1,55 @@ +import { + Button, + Flex, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from "@chakra-ui/react"; +import { CodeEditor, CodeEditorImperativeAttributes } from "kurtosis-ui-components"; +import { useMemo, useRef } from "react"; + +type EditFileModalProps = { + isOpen: boolean; + onClose: () => void; + filePath: string[]; + file: string; + onSave: (newContents: string) => void; +}; +export const EditFileModal = ({ isOpen, onClose, filePath, file, onSave }: EditFileModalProps) => { + const codeEditorRef = useRef(null); + + const fileName = useMemo(() => "/" + filePath.join("/"), [filePath]); + + return ( + + + + + Editing {fileName} + + + + + + + + + + + + + + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/NewFileModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/NewFileModal.tsx new file mode 100644 index 0000000000..36f71b1c3b --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/NewFileModal.tsx @@ -0,0 +1,61 @@ +import { + Button, + Flex, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from "@chakra-ui/react"; +import { FormProvider, useForm } from "react-hook-form"; +import { KurtosisFormControl } from "../../../../form/KurtosisFormControl"; +import { StringArgumentInput } from "../../../../form/StringArgumentInput"; + +type NewFileModalProps = { + isOpen: boolean; + onClose: () => void; + onConfirm: (newFileName: string) => void; +}; +export const NewFileModal = ({ isOpen, onClose, onConfirm }: NewFileModalProps) => { + const formMethods = useForm<{ fileName: string }>({ + defaultValues: { fileName: "" }, + }); + + const onValidSubmit = (data: { fileName: string }) => { + onConfirm(data.fileName); + }; + + return ( + + + + + Create a New File + + + + + + + + + + + + + + + + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts new file mode 100644 index 0000000000..e17cd84d48 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts @@ -0,0 +1,5 @@ +export type Variable = { + id: string; + displayName: string; + value: string; +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts new file mode 100644 index 0000000000..4ee26fec42 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts @@ -0,0 +1,245 @@ +import { isDefined, RemoveFunctions, stringifyError } from "kurtosis-ui-components"; +import { Edge, Node } from "reactflow"; +import { Result } from "true-myth"; +import { EnclaveFullInfo } from "../../../types"; +import { Variable } from "./types"; +import { KurtosisNodeData, KurtosisServiceNodeData } from "./VariableContextProvider"; + +export const EMUI_BUILD_STATE_KEY = "EMUI_BUILD_STATE"; + +export function starlarkScriptContainsEMUIBuildState(script: string) { + return script.includes(EMUI_BUILD_STATE_KEY); +} + +export function getInitialGraphStateFromEnclave( + enclave?: RemoveFunctions, +): Result<{ nodes: Node[]; edges: Edge[]; data: Record }, string> { + if (!isDefined(enclave)) { + return Result.ok({ nodes: [], edges: [], data: {} }); + } + if (!isDefined(enclave.starlarkRun)) { + return Result.err("Enclave has no previous starlark run."); + } + if (enclave.starlarkRun.isErr) { + return Result.err(`Fetching previous starlark run resulted in an error: ${enclave.starlarkRun.error}`); + } + const b64State = enclave.starlarkRun.value.serializedScript + .split("\n") + .find((line) => line.includes(EMUI_BUILD_STATE_KEY)); + if (!isDefined(b64State)) { + return Result.err("Enclave wasn't created with the EMUI enclave builder."); + } + try { + return Result.ok(JSON.parse(atob(b64State.split("=")[1]))); + } catch (error: any) { + return Result.err(`Couldn't parse previous state: ${stringifyError(error)}`); + } +} + +function normaliseNameToStarlarkVariable(name: string) { + return name.replace(/\s|-/g, "_").toLowerCase(); +} + +const variablePattern = /\{\{((?:service|artifact).([^.]+)\.?.*)}}/; +export function getVariablesFromNodes(nodes: Record): Variable[] { + return Object.entries(nodes).flatMap(([id, data]) => + data.type === "service" + ? [ + { + id: `service.${id}.name`, + displayName: `service.${data.serviceName}.name`, + value: `${normaliseNameToStarlarkVariable(data.serviceName)}.name`, + }, + { + id: `service.${id}.hostname`, + displayName: `service.${data.serviceName}.hostname`, + value: `${normaliseNameToStarlarkVariable(data.serviceName)}.hostname`, + }, + ...data.ports.flatMap((port, i) => [ + { + id: `service.${id}.port.${i}`, + displayName: `service.${data.serviceName}.port.${port.portName}`, + value: `"{}://{}:{}".format(${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${ + port.portName + }"].application_protocol, ${normaliseNameToStarlarkVariable( + data.serviceName, + )}.hostname, ${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${port.portName}"].number)`, + }, + { + id: `service.${id}.port.${i}.port`, + displayName: `service.${data.serviceName}.port.${port.portName}.port`, + value: `${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${port.portName}"].number`, + }, + { + id: `service.${id}.port.${i}.applicationProtocol`, + displayName: `service.${data.serviceName}.port.${port.portName}.application_protocol`, + value: `${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${ + port.portName + }"].application_protocol`, + }, + ]), + ...data.env.map((env, i) => ({ + id: `service.${id}.env.${i}`, + displayName: `service.${data.serviceName}.env.${env.key}`, + value: `"${env.value}"`, + })), + ] + : [ + { + id: `artifact.${id}`, + displayName: `artifact.${data.artifactName}`, + value: `${normaliseNameToStarlarkVariable(data.artifactName)}`, + }, + ], + ); +} + +export function getNodeDependencies(nodes: Record): Record> { + const dependencies: Record> = {}; + const getDependenciesFor = (key: string): Set => { + if (!isDefined(dependencies[key])) { + dependencies[key] = new Set(); + } + return dependencies[key]; + }; + Object.entries(nodes).forEach(([id, data]) => { + if (data.type === "service") { + const nameMatches = data.serviceName.match(variablePattern); + if (nameMatches) { + getDependenciesFor(id).add(nameMatches[2]); + } + data.env.forEach((env) => { + const envMatches = env.key.match(variablePattern) || env.value.match(variablePattern); + if (envMatches) { + getDependenciesFor(id).add(envMatches[2]); + } + }); + data.ports.forEach((port) => { + const portMatches = port.portName.match(variablePattern) || port.applicationProtocol.match(variablePattern); + if (portMatches) { + getDependenciesFor(id).add(portMatches[2]); + } + }); + data.files.forEach((file) => { + const fileMatches = file.mountPoint.match(variablePattern) || file.artifactName.match(variablePattern); + if (fileMatches) { + getDependenciesFor(id).add(fileMatches[2]); + } + }); + } + }); + return dependencies; +} + +export function generateStarlarkFromGraph( + nodes: Node[], + edges: Edge[], + data: Record, + existingEnclave?: RemoveFunctions, +): string { + // Topological sort + const sortedNodes: Node[] = []; + let remainingEdges = [...edges]; + while (remainingEdges.length > 0 || sortedNodes.length !== nodes.length) { + const nodesRemoved = nodes + .filter((node) => remainingEdges.every((edge) => edge.target !== node.id)) // eslint-disable-line no-loop-func + .filter((node) => !sortedNodes.includes(node)); + if (nodesRemoved.length === 0) { + throw new Error( + "Topological sort couldn't remove nodes. This indicates a cycle has been detected. Starlark cannot be rendered.", + ); + } + sortedNodes.push(...nodesRemoved); + remainingEdges = remainingEdges.filter((edge) => sortedNodes.every((node) => edge.source !== node.id)); + } + const variablesById = getVariablesFromNodes(data).reduce( + (acc, cur) => ({ ...acc, [cur.id]: cur }), + {} as Record, + ); + const interpolateValue = (input: string): string => { + let formatString = input; + let variableMatches = formatString.match(variablePattern); + if (!isDefined(variableMatches)) { + return `"${formatString}"`; + } + + const references: string[] = []; + while (isDefined(variableMatches)) { + formatString = formatString.replace(variableMatches[0], "{}"); + references.push(variablesById[variableMatches[1]].value); + variableMatches = formatString.match(variablePattern); + } + if (formatString === "{}") { + return references[0]; + } + + return `"${formatString}".format(${references.join(", ")})`; + }; + + let starlark = "def run(plan):\n"; + for (const node of sortedNodes) { + const nodeData = data[node.id]; + if (nodeData.type === "service") { + const serviceName = normaliseNameToStarlarkVariable(nodeData.serviceName); + starlark += ` ${serviceName} = plan.add_service(\n`; + starlark += ` name = ${interpolateValue(nodeData.serviceName)},\n`; + starlark += ` config = ServiceConfig (\n`; + starlark += ` image = ${interpolateValue(nodeData.image)},\n`; + starlark += ` ports = {\n`; + for (const { portName, port, applicationProtocol, transportProtocol } of nodeData.ports) { + starlark += ` ${interpolateValue(portName)}: PortSpec(\n`; + starlark += ` number = ${port},\n`; + starlark += ` transport_protocol = "${transportProtocol}",\n`; + starlark += ` application_protocol = ${interpolateValue(applicationProtocol)},\n`; + starlark += ` ),\n`; + } + starlark += ` },\n`; + starlark += ` env_vars = {\n`; + for (const { key, value } of nodeData.env) { + starlark += ` ${interpolateValue(key)}: ${interpolateValue(value)},\n`; + } + starlark += ` },\n`; + starlark += ` files = {\n`; + for (const { mountPoint, artifactName } of nodeData.files) { + starlark += ` ${interpolateValue(mountPoint)}: ${interpolateValue(artifactName)},\n`; + } + starlark += ` },\n`; + starlark += ` ),\n`; + starlark += ` )\n\n`; + } + + if (nodeData.type === "artifact") { + const artifactName = normaliseNameToStarlarkVariable(nodeData.artifactName); + starlark += ` ${artifactName} = plan.render_templates(\n`; + starlark += ` name = "${nodeData.artifactName}",\n`; + starlark += ` config = {\n`; + for (const [fileName, fileText] of Object.entries(nodeData.files)) { + starlark += ` "${fileName}": struct(\n`; + starlark += ` template="""${fileText}""",\n`; + starlark += ` data={},\n`; + starlark += ` ),\n`; + } + starlark += ` },\n`; + starlark += ` )\n\n`; + } + } + + // Delete any services from any existing enclave that aren't defined anymore + if (isDefined(existingEnclave) && existingEnclave.services?.isOk) { + for (const existingService of Object.values(existingEnclave.services.value.serviceInfo)) { + const serviceNoLongerExists = sortedNodes.every((node) => { + const nodeData = data[node.id]; + return nodeData.type !== "service" || nodeData.serviceName !== existingService.name; + }); + if (serviceNoLongerExists) { + starlark += ` plan.remove_service(name = "${existingService.name}")\n`; + } + } + } + + starlark += `\n\n# ${EMUI_BUILD_STATE_KEY}=${btoa(JSON.stringify({ nodes, edges, data }))}`; + + console.log(starlark); + + return starlark; +} diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/CreateEnclaveButton.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/CreateEnclaveButton.tsx index 1b6ceaa4ae..fc2bec4e60 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/CreateEnclaveButton.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/CreateEnclaveButton.tsx @@ -1,33 +1,36 @@ -import { Button, Menu, MenuButton, Tooltip } from "@chakra-ui/react"; -import { FiPlus } from "react-icons/fi"; +import { Button, ButtonGroup, Tooltip } from "@chakra-ui/react"; +import { FiPlus, FiTool } from "react-icons/fi"; import { useNavigate } from "react-router-dom"; -import { KURTOSIS_CREATE_ENCLAVE_URL_ARG } from "../configuration/drawer/constants"; +import { useSettings } from "../../../settings"; +import { KURTOSIS_BUILD_ENCLAVE_URL_ARG, KURTOSIS_CREATE_ENCLAVE_URL_ARG } from "../configuration/drawer/constants"; export const CreateEnclaveButton = () => { + const { settings } = useSettings(); const navigate = useNavigate(); return ( - <> -

- - } + + {settings.ENABLE_EXPERIMENTAL_BUILD_ENCLAVE && ( + + - {/**/} - {/* navigate(`#${KURTOSIS_CREATE_ENCLAVE_URL_ARG}`)} icon={}>*/} - {/* Manual*/} - {/* */} - {/* navigate("/catalog")} icon={}>*/} - {/* Catalog*/} - {/* */} - {/**/} - - + )} + + + + ); }; diff --git a/enclave-manager/web/packages/app/src/emui/settings.tsx b/enclave-manager/web/packages/app/src/emui/settings.tsx new file mode 100644 index 0000000000..03f88a83c9 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/settings.tsx @@ -0,0 +1,66 @@ +import { isDefined, maybeParse } from "kurtosis-ui-components"; +import { createContext, PropsWithChildren, useCallback, useContext, useEffect, useState } from "react"; + +type Settings = { + ENABLE_EXPERIMENTAL_BUILD_ENCLAVE: boolean; + SAVED_PACKAGES: string[]; +}; + +export const settingKeys: { [k in keyof Settings]: k } = { + ENABLE_EXPERIMENTAL_BUILD_ENCLAVE: "ENABLE_EXPERIMENTAL_BUILD_ENCLAVE", + SAVED_PACKAGES: "SAVED_PACKAGES", +} as const; + +const defaultSettings: Settings = { ENABLE_EXPERIMENTAL_BUILD_ENCLAVE: false, SAVED_PACKAGES: [] }; + +const SETTINGS_LOCAL_STORAGE_KEY = "kurtosis-settings"; + +export const storeSettings = (settings: Settings) => { + localStorage.setItem(SETTINGS_LOCAL_STORAGE_KEY, JSON.stringify(settings)); +}; + +export const loadSettings = (): Settings => { + // TODO: Remove once confident all users have migrated from the old settings key + const oldSavedPackages = localStorage.getItem("kurtosis-saved-packages"); + const migratedDefaultSettings = { + ...defaultSettings, + SAVED_PACKAGES: isDefined(oldSavedPackages) + ? maybeParse(oldSavedPackages, defaultSettings.SAVED_PACKAGES) + : defaultSettings.SAVED_PACKAGES, + }; + + const savedRawValue = localStorage.getItem(SETTINGS_LOCAL_STORAGE_KEY); + + if (!isDefined(savedRawValue)) { + return migratedDefaultSettings; + } + + return maybeParse(savedRawValue, migratedDefaultSettings); +}; + +type SettingsContextState = { + settings: Settings; + updateSetting: (setting: S, value: Settings[S]) => void; +}; + +const SettingsContext = createContext(null as any); + +export const SettingsContextProvider = ({ children }: PropsWithChildren) => { + const [settings, setSettings] = useState(defaultSettings); + + const updateSetting = useCallback((setting: S, value: Settings[S]) => { + setSettings((settings) => { + const newSettings = { ...settings, [setting]: value }; + storeSettings(newSettings); + return newSettings; + }); + }, []); + + useEffect(() => { + setSettings(loadSettings()); + }, []); + + return {children}; +}; + +export const useSettings = () => useContext(SettingsContext); diff --git a/enclave-manager/web/packages/components/src/CodeEditor.tsx b/enclave-manager/web/packages/components/src/CodeEditor.tsx index 1b234d3575..3b5147b645 100644 --- a/enclave-manager/web/packages/components/src/CodeEditor.tsx +++ b/enclave-manager/web/packages/components/src/CodeEditor.tsx @@ -10,6 +10,7 @@ const MONACO_READ_ONLY_CHANGE_EVENT_ID = 89; type CodeEditorProps = { text: string; fileName?: string; + isEditable?: boolean; onTextChange?: (newText: string) => void; showLineNumbers?: boolean; }; @@ -17,12 +18,13 @@ type CodeEditorProps = { export type CodeEditorImperativeAttributes = { formatCode: () => Promise; setText: (text: string) => void; + getText: () => string; setLanguage: (language: string) => void; }; export const CodeEditor = forwardRef( - ({ text, fileName, onTextChange, showLineNumbers }, ref) => { - const isReadOnly = !isDefined(onTextChange); + ({ text, fileName, isEditable, onTextChange, showLineNumbers }, ref) => { + const isReadOnly = !isEditable && !isDefined(onTextChange); const [monaco, setMonaco] = useState(); const [editor, setEditor] = useState(); @@ -56,8 +58,8 @@ export const CodeEditor = forwardRef { if (isDefined(value) && onTextChange) { onTextChange(value); - resizeEditorBasedOnContent(); } + resizeEditorBasedOnContent(); }; useImperativeHandle( @@ -111,6 +113,9 @@ export const CodeEditor = forwardRef { + return editor?.getValue() || ""; + }, setLanguage: (language: string) => { if (!isDefined(editor) || !isDefined(monaco)) { return; diff --git a/enclave-manager/web/packages/components/src/FileTree.tsx b/enclave-manager/web/packages/components/src/FileTree.tsx index c67ea182ac..a917160320 100644 --- a/enclave-manager/web/packages/components/src/FileTree.tsx +++ b/enclave-manager/web/packages/components/src/FileTree.tsx @@ -21,11 +21,18 @@ type FileTreeProps = { nodes: FileTreeNode[]; selectedFilePath?: string[]; onFileSelected: (selectedFilePath: string[]) => void; + onFileDblClicked?: (selectedFilePath: string[]) => void; // Internal prop used for padding _isChildNode?: boolean; }; -export const FileTree = ({ nodes, selectedFilePath, onFileSelected, _isChildNode }: FileTreeProps) => { +export const FileTree = ({ + nodes, + selectedFilePath, + onFileSelected, + onFileDblClicked, + _isChildNode, +}: FileTreeProps) => { return ( {nodes.map((node, i) => ( @@ -38,6 +45,7 @@ export const FileTree = ({ nodes, selectedFilePath, onFileSelected, _isChildNode : undefined } onFileSelected={onFileSelected} + onFileDblClicked={onFileDblClicked} /> ))} @@ -48,6 +56,7 @@ type FileTreeNodeComponentProps = { node: FileTreeNode; selectedFilePath?: string[]; onFileSelected: (selectedFilePath: string[]) => void; + onFileDblClicked?: (selectedFilePath: string[]) => void; }; const FileTreeNodeComponent = React.memo((props: FileTreeNodeComponentProps) => { @@ -62,6 +71,7 @@ const DirectoryNode = ({ node, selectedFilePath, onFileSelected, + onFileDblClicked, }: FileTreeNodeComponentProps & { node: { childNodes: FileTreeNode[] } }) => { const [collapsed, setCollapsed] = useState(false); @@ -82,6 +92,13 @@ const DirectoryNode = ({ [onFileSelected, node], ); + const handleFileDblClicked = useMemo(() => { + if (!isDefined(onFileDblClicked)) { + return undefined; + } + return (filePath: string[]) => onFileDblClicked([node.name, ...filePath]); + }, [onFileDblClicked]); + return ( <> + + setCurrentStarlarkPreview(undefined)} + starlark={currentStarlarkPreview} + /> ); }; @@ -225,7 +237,8 @@ const Visualiser = forwardRef( addNodes({ id, position: getNewNodePosition(), - width: 600, + width: 650, + style: { width: "650px" }, type: "serviceNode", data: {}, }); @@ -238,6 +251,7 @@ const Visualiser = forwardRef( id, position: getNewNodePosition(), width: 600, + style: { width: "400px" }, type: "artifactNode", data: {}, }); @@ -257,6 +271,27 @@ const Visualiser = forwardRef( }); }, [setEdges, data]); + // Remove the resizeObserver error + useEffect(() => { + const errorHandler = (e: any) => { + if ( + e.message.includes( + "ResizeObserver loop completed with undelivered notifications" || "ResizeObserver loop limit exceeded", + ) + ) { + const resizeObserverErr = document.getElementById("webpack-dev-server-client-overlay"); + if (resizeObserverErr) { + resizeObserverErr.style.display = "none"; + } + } + }; + window.addEventListener("error", errorHandler); + + return () => { + window.removeEventListener("error", errorHandler); + }; + }, []); + useImperativeHandle( ref, () => ({ @@ -275,7 +310,7 @@ const Visualiser = forwardRef( Add Service Node diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx index 58c62bdab6..ea3722fd38 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx @@ -39,7 +39,7 @@ export const KurtosisArtifactNode = memo( flexDirection={"column"} height={"100%"} borderRadius={"8px"} - p={selected ? "8px" : "10px"} + p={"10px"} bg={"gray.600"} borderWidth={"3px"} outline={"3px solid transparent"} @@ -92,8 +92,8 @@ export const KurtosisArtifactNode = memo( className={"nodrag nowheel"} gap={"8px"} > - name={"artifactName"} label={"Artifact Name"}> - + name={"artifactName"} label={"Artifact Name"} isRequired> + diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx index 8f164c2325..6c16611d97 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx @@ -1,4 +1,4 @@ -import { Flex, Grid, GridItem, IconButton, Text } from "@chakra-ui/react"; +import { Flex, Grid, GridItem, IconButton, Tab, TabList, TabPanel, TabPanels, Tabs, Text } from "@chakra-ui/react"; import { memo, useMemo } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { FiTrash } from "react-icons/fi"; @@ -54,7 +54,7 @@ export const KurtosisServiceNode = memo( flexDirection={"column"} height={"100%"} borderRadius={"8px"} - p={selected ? "8px" : "10px"} + p={"10px"} bg={"gray.600"} borderWidth={"3px"} outline={"3px solid transparent"} @@ -101,135 +101,155 @@ export const KurtosisServiceNode = memo( - name={"serviceName"} label={"Service Name"} isRequired> - { - if (typeof val !== "string") { - return "Value should be a string"; - } - if (!val.match(/^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$/)) { - return ( - "Service names must adhere to the RFC 1035 standard, specifically implementing this regex and" + - " be 1-63 characters long: ^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$. This means the service name must " + - "only contain lowercase alphanumeric characters or '-', and must start with a lowercase alphabet " + - "and end with a lowercase alphanumeric" - ); - } - }} - /> - - name={"image"} label={"Container Image"} isRequired> - { - if (typeof val !== "string") { - return "Value should be a string"; - } - if ( - !val.match( - /^(?[\w.\-_]+((?::\d+|)(?=\/[a-z0-9._-]+\/[a-z0-9._-]+))|)(?:\/|)(?[a-z0-9.\-_]+(?:\/[a-z0-9.\-_]+|))(:(?[\w.\-_]{1,127})|)$/gim, - ) - ) { - return "Value does not look like a docker image"; - } - }} - /> - - name={"env"} label={"Environment Variables"}> - - name={"env"} - KeyFieldComponent={StringArgumentInput} - ValueFieldComponent={MentionStringArgumentInput} - /> - - name={"ports"} label={"Ports"}> - ( - - - - {...props} - size={"sm"} - placeholder={"Port Name (eg postgres)"} - name={`${props.name as `ports.${number}`}.portName`} - /> - - - - {...props} - size={"sm"} - placeholder={"Application Protocol (eg postgresql)"} - name={`${props.name as `ports.${number}`}.applicationProtocol`} - /> - - - - {...props} - options={["TCP", "UDP"]} - name={`${props.name as `ports.${number}`}.transportProtocol`} - /> - - - - {...props} - name={`${props.name as `ports.${number}`}.port`} - size={"sm"} - /> - - - )} - createNewValue={(): KurtosisPort => ({ - portName: "", - applicationProtocol: "", - transportProtocol: "TCP", - port: 0, - })} - /> - - - name={"files"} - label={"Files"} - helperText={"Choose where to mount artifacts on this services filesystem"} - > - ( - - - - {...props} - size={"sm"} - placeholder={"/some/path"} - name={`${props.name as `files.${number}`}.mountPoint`} - /> - - - - options={artifactVariableOptions} - {...props} - size={"sm"} - placeholder={"Select an Artifact"} - name={`${props.name as `files.${number}`}.artifactName`} - /> - - - )} - createNewValue={(): KurtosisFileMount => ({ - mountPoint: "", - artifactName: "", - })} - /> - + + name={"serviceName"} label={"Service Name"} isRequired> + { + if (typeof val !== "string") { + return "Value should be a string"; + } + if (!val.match(/^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$/)) { + return ( + "Service names must adhere to the RFC 1035 standard, specifically implementing this regex and" + + " be 1-63 characters long: ^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$. This means the service name must " + + "only contain lowercase alphanumeric characters or '-', and must start with a lowercase alphabet " + + "and end with a lowercase alphanumeric" + ); + } + }} + /> + + name={"image"} label={"Container Image"} isRequired> + { + if (typeof val !== "string") { + return "Value should be a string"; + } + if ( + !val.match( + /^(?[\w.\-_]+((?::\d+|)(?=\/[a-z0-9._-]+\/[a-z0-9._-]+))|)(?:\/|)(?[a-z0-9.\-_]+(?:\/[a-z0-9.\-_]+|))(:(?[\w.\-_]{1,127})|)$/gim, + ) + ) { + return "Value does not look like a docker image"; + } + }} + /> + + + + + Environment Variables + Ports + Files + + + + + name={"env"} label={"Environment Variables"}> + + name={"env"} + KeyFieldComponent={StringArgumentInput} + ValueFieldComponent={MentionStringArgumentInput} + /> + + + + name={"ports"} label={"Ports"}> + ( + + + + {...props} + size={"sm"} + placeholder={"Port Name (eg postgres)"} + name={`${props.name as `ports.${number}`}.portName`} + /> + + + + {...props} + size={"sm"} + placeholder={"Application Protocol (eg postgresql)"} + name={`${props.name as `ports.${number}`}.applicationProtocol`} + /> + + + + {...props} + options={["TCP", "UDP"]} + name={`${props.name as `ports.${number}`}.transportProtocol`} + /> + + + + {...props} + name={`${props.name as `ports.${number}`}.port`} + size={"sm"} + /> + + + )} + createNewValue={(): KurtosisPort => ({ + portName: "", + applicationProtocol: "", + transportProtocol: "TCP", + port: 0, + })} + /> + + + + + name={"files"} + label={"Files"} + helperText={"Choose where to mount artifacts on this services filesystem"} + > + ( + + + + {...props} + size={"sm"} + placeholder={"/some/path"} + name={`${props.name as `files.${number}`}.mountPoint`} + /> + + + + options={artifactVariableOptions} + {...props} + size={"sm"} + placeholder={"Select an Artifact"} + name={`${props.name as `files.${number}`}.artifactName`} + /> + + + )} + createNewValue={(): KurtosisFileMount => ({ + mountPoint: "", + artifactName: "", + })} + /> + + + + diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/FileTreeArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/FileTreeArgumentInput.tsx index dd9c68a5c4..dc9cb78434 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/FileTreeArgumentInput.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/FileTreeArgumentInput.tsx @@ -4,8 +4,8 @@ import { useMemo, useState } from "react"; import { Controller } from "react-hook-form"; import { FiPlus } from "react-icons/fi"; import { KurtosisFormInputProps } from "../../../form/types"; -import { EditFileModal } from "./modals/EditFileModal"; -import { NewFileModal } from "./modals/NewFileModal"; +import { EditFileModal } from "../modals/EditFileModal"; +import { NewFileModal } from "../modals/NewFileModal"; type FileTreeArgumentInputProps = KurtosisFormInputProps; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/EditFileModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/EditFileModal.tsx similarity index 100% rename from enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/EditFileModal.tsx rename to enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/EditFileModal.tsx diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/NewFileModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/NewFileModal.tsx similarity index 92% rename from enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/NewFileModal.tsx rename to enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/NewFileModal.tsx index 36f71b1c3b..db206cc665 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/modals/NewFileModal.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/NewFileModal.tsx @@ -10,8 +10,8 @@ import { ModalOverlay, } from "@chakra-ui/react"; import { FormProvider, useForm } from "react-hook-form"; -import { KurtosisFormControl } from "../../../../form/KurtosisFormControl"; -import { StringArgumentInput } from "../../../../form/StringArgumentInput"; +import { KurtosisFormControl } from "../../../form/KurtosisFormControl"; +import { StringArgumentInput } from "../../../form/StringArgumentInput"; type NewFileModalProps = { isOpen: boolean; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/ViewStarlarkModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/ViewStarlarkModal.tsx new file mode 100644 index 0000000000..3b408ce5ac --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/ViewStarlarkModal.tsx @@ -0,0 +1,39 @@ +import { + Button, + Flex, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from "@chakra-ui/react"; +import { FileDisplay, isDefined } from "kurtosis-ui-components"; + +type ViewStarlarkModalProps = { + isOpen: boolean; + onClose: () => void; + starlark?: string; +}; +export const ViewStarlarkModal = ({ isOpen, onClose, starlark }: ViewStarlarkModalProps) => { + return ( + + + + Previewing Starlark + + + {isDefined(starlark) && } + + + + + + + + + ); +}; From 0eae9fc942605f14a04554752a0c0dca7b02b1f7 Mon Sep 17 00:00:00 2001 From: Edgar Gomes Date: Fri, 9 Feb 2024 16:21:54 -0300 Subject: [PATCH 048/102] feat: build nix image (#2132) ## Description: Add ability to build Nix images inside APIC. ## Is this change user facing? YES --- cli/cli/commands/import/import.go | 10 +- .../docker_kurtosis_backend.go | 10 +- .../docker/docker_manager/docker_manager.go | 72 +++++- .../engine_functions/create_engine.go | 3 + .../kubernetes_kurtosis_backend.go | 9 +- ...urtosis_backend_api_container_functions.go | 10 +- .../start_user_services.go | 6 +- .../metrics_reporting_kurtosis_backend.go | 10 +- .../lib/backend_interface/kurtosis_backend.go | 8 +- .../mock_kurtosis_backend.go | 70 +++++- .../objects/nix_build_spec/nix_build_spec.go | 70 ++++++ .../objects/service/service_config.go | 10 + .../objects/service/service_config_test.go | 11 +- .../service_registration/repository_test.go | 12 +- .../lib/image_utils/image_utils.go | 72 ++++++ core/server/Dockerfile | 3 +- core/server/Dockerfile.debug | 3 +- .../docker_compose_transpiler.go | 12 +- .../default_service_network_test.go | 1 + .../startosis_engine/kurtosis_builtins.go | 1 + .../add_service/add_service_shared.go | 6 +- .../add_service/add_service_shared_test.go | 9 +- .../tasks/tasks_shared.go | 8 +- .../test_engine/add_service_framework_test.go | 4 +- .../add_services_framework_test.go | 5 +- .../nix_build_spec_framework_test.go | 67 +++++ .../service_config_image_build_spec_test.go | 4 +- ...service_config_image_registry_spec_test.go | 4 +- .../service_config_minimal_framework_test.go | 4 +- .../service_config_toleration_test.go | 4 +- .../test_engine/static_constants.go | 9 + .../service_config/nix_build_spec.go | 232 ++++++++++++++++++ .../service_config/service_config.go | 33 ++- .../startosis_validator/images_validator.go | 42 +++- .../validator_environment.go | 9 +- .../starlark-reference/nix-support.md | 52 ++++ .../starlark-reference/service-config.md | 23 ++ enclave-manager/server/server.go | 2 + .../server/engine/streaming/websocket_pump.go | 1 + flake.nix | 1 + 40 files changed, 852 insertions(+), 70 deletions(-) create mode 100644 container-engine-lib/lib/backend_interface/objects/nix_build_spec/nix_build_spec.go create mode 100644 container-engine-lib/lib/image_utils/image_utils.go create mode 100644 core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/nix_build_spec_framework_test.go create mode 100644 core/server/api_container/server/startosis_engine/kurtosis_types/service_config/nix_build_spec.go create mode 100644 docs/docs/api-reference/starlark-reference/nix-support.md diff --git a/cli/cli/commands/import/import.go b/cli/cli/commands/import/import.go index 13583d192f..6058d95cc5 100644 --- a/cli/cli/commands/import/import.go +++ b/cli/cli/commands/import/import.go @@ -3,6 +3,11 @@ package _import import ( "context" "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" "github.com/joho/godotenv" @@ -25,10 +30,6 @@ import ( "github.com/kurtosis-tech/kurtosis/name_generator" "github.com/kurtosis-tech/stacktrace" "github.com/sirupsen/logrus" - "os" - "path/filepath" - "strconv" - "strings" ) const ( @@ -175,6 +176,7 @@ func run( func convertComposeFileToStarlark(path string, dotEnvMap map[string]string) (string, map[string]string, error) { project, err := loader.Load(types.ConfigDetails{ //nolint:exhaustruct + // nolint: exhaustruct ConfigFiles: []types.ConfigFile{{Filename: path}}, Environment: dotEnvMap, }) diff --git a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go index 7632be76aa..d663de40a9 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/docker_kurtosis_backend.go @@ -2,11 +2,13 @@ package docker_kurtosis_backend import ( "context" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "io" "sync" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" + "github.com/sirupsen/logrus" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/engine_functions" @@ -544,6 +546,10 @@ func (backend *DockerKurtosisBackend) BuildImage(ctx context.Context, imageName return backend.dockerManager.BuildImage(ctx, imageName, imageBuildSpec) } +func (backend *DockerKurtosisBackend) NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) { + return backend.dockerManager.NixBuild(ctx, nixBuildSpec) +} + // ==================================================================================================== // // Private helper functions shared by multiple subfunctions files diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go index ad5e386806..cdcf06fac3 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go @@ -9,22 +9,28 @@ import ( "bufio" "bytes" "context" + "crypto/md5" "encoding/json" "fmt" - "github.com/docker/docker/api/types/registry" - "github.com/docker/go-units" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/uuid_generator" - "github.com/kurtosis-tech/kurtosis/utils" "io" "math" "net" + "os" + "os/exec" "regexp" "strings" "sync" "time" + "github.com/docker/docker/api/types/registry" + "github.com/docker/go-units" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/image_utils" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/uuid_generator" + "github.com/kurtosis-tech/kurtosis/utils" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" @@ -152,6 +158,8 @@ const ( // Per https://github.com/hashicorp/waypoint/pull/1937/files buildkitSessionSharedKey = "" + + nixCmdPath = "/nix/var/nix/profiles/default/bin/nix" ) type RestartPolicy string @@ -1313,6 +1321,58 @@ func (manager *DockerManager) FetchImage(ctx context.Context, image string, regi return pulledFromRemote, imageArchitecture, nil } +func (manager *DockerManager) NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) { + flakeReference := nixBuildSpec.GetFullFlakeReference() + + // Flake generates a link to the nix store containing the image result, to avoid collision with a possible existing one from the + // build context (from the user env) and which would result when trying to overwrite it, we create a unique one + hasher := md5.New() + hasher.Write([]byte(flakeReference)) + resultLink := fmt.Sprintf("nix-result-%x", hasher.Sum(nil)) + + cmd := exec.Command( + nixCmdPath, "build", flakeReference, + "--print-out-paths", + "--extra-experimental-features", "flakes nix-command", + "--out-link", resultLink) + + var errBuffer strings.Builder + cmd.Stderr = &errBuffer + imageFileRaw, err := cmd.Output() + if err != nil { + errMsg := errBuffer.String() + logrus.WithError(err).Error(errMsg) + return "", stacktrace.Propagate(err, "Failed to build nix image with Nix.") + } + imageFile := strings.TrimSpace(string(imageFileRaw)) + logrus.Debugf("Nix flake image on attribute %s, result on image file %s", flakeReference, imageFile) + + imageTags, err := image_utils.GetRepoTags(imageFile) + if err != nil { + return "", stacktrace.Propagate(err, "Failed to get image tags from Nix image %s", imageFile) + } + + if len(imageTags) == 0 { + return "", stacktrace.NewError("Generated image %s did not have any tags", imageFile) + } else if len(imageTags) > 1 { + logrus.Warnf("Generated image %s had multiple tags: %v. We'll select the first.", imageFile, imageTags) + } + imageTag := imageTags[0] + + image, err := os.Open(imageFile) + if err != nil { + return "", stacktrace.Propagate(err, "Failed to open generated Nix image on %s", imageFile) + } + + _, err = manager.dockerClient.ImageLoad(ctx, image, false) + if err != nil { + return "", stacktrace.Propagate(err, "Failed to load Nix image %s in docker", imageFile) + } + logrus.Debugf("Nix generated image file %s is loaded into docker", imageFile) + + return imageTag, nil +} + func (manager *DockerManager) BuildImage(ctx context.Context, imageName string, imageBuildSpec *image_build_spec.ImageBuildSpec) (string, error) { buildContextDirPath := imageBuildSpec.GetBuildContextDir() buildContextTarReader, err := getBuildContextReader(buildContextDirPath) diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go index ca132b4d1c..3c8f9e9207 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions/create_engine.go @@ -321,6 +321,7 @@ func createEngineClusterRole( } clusterRoleName := clusterRolesAttributes.GetName().GetString() clusterRoleLabels := shared_helpers.GetStringMapFromLabelMap(clusterRolesAttributes.GetLabels()) + // nolint: exhaustruct clusterRolePolicyRules := []rbacv1.PolicyRule{ { Verbs: []string{ @@ -388,6 +389,7 @@ func createEngineClusterRoleBindings( } clusterRoleBindingsName := clusterRoleBindingsAttributes.GetName().GetString() clusterRoleBindingsLabels := shared_helpers.GetStringMapFromLabelMap(clusterRoleBindingsAttributes.GetLabels()) + // nolint: exhaustruct clusterRoleBindingsSubjects := []rbacv1.Subject{ { Kind: rbacv1.ServiceAccountKind, @@ -452,6 +454,7 @@ func createEnginePod( } engineContainerEnvVars = append(engineContainerEnvVars, envVar) } + // nolint: exhaustruct engineContainers := []apiv1.Container{ { Name: kurtosisEngineContainerName, diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go index d87cd0b358..8f835e4e7e 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend.go @@ -2,9 +2,11 @@ package kubernetes_kurtosis_backend import ( "context" + "io" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" - "io" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" apiv1 "k8s.io/api/core/v1" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions" @@ -487,6 +489,11 @@ func (backend *KubernetesKurtosisBackend) BuildImage(ctx context.Context, imageN return "", stacktrace.NewError("Building images isn't yet implemented in Kubernetes.") } +func (backend *KubernetesKurtosisBackend) NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) { + // TODO IMPLEMENT + return "", stacktrace.NewError("Nix image building isn't yet implemented in Kubernetes.") +} + // ==================================================================================================== // // Private Helper Functions diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go index 31b7f861cb..0cfe1986f4 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/kubernetes_kurtosis_backend_api_container_functions.go @@ -3,6 +3,9 @@ package kubernetes_kurtosis_backend import ( "context" "fmt" + "net" + "time" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/consts" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/shared_helpers" kubernetes_manager_consts "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/consts" @@ -17,8 +20,6 @@ import ( apiv1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" applyconfigurationsv1 "k8s.io/client-go/applyconfigurations/core/v1" - "net" - "time" ) const ( @@ -233,6 +234,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( clusterRoleName := clusterRolesAttributes.GetName().GetString() clusterRoleLabels := shared_helpers.GetStringMapFromLabelMap(clusterRolesAttributes.GetLabels()) + // nolint: exhaustruct clusterRolePolicyRules := []rbacv1.PolicyRule{ { Verbs: []string{ @@ -292,6 +294,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( clusterRoleBindingName := clusterRoleBindingsAttributes.GetName().GetString() clusterRoleBindingsLabels := shared_helpers.GetStringMapFromLabelMap(clusterRoleBindingsAttributes.GetLabels()) + // nolint: exhaustruct clusterRoleBindingsSubjects := []rbacv1.Subject{ { Kind: rbacv1.ServiceAccountKind, @@ -335,6 +338,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( roleName := rolesAttributes.GetName().GetString() roleLabels := shared_helpers.GetStringMapFromLabelMap(rolesAttributes.GetLabels()) + // nolint: exhaustruct rolePolicyRules := []rbacv1.PolicyRule{ { Verbs: []string{ @@ -394,6 +398,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer( roleBindingName := roleBindingsAttributes.GetName().GetString() roleBindingsLabels := shared_helpers.GetStringMapFromLabelMap(roleBindingsAttributes.GetLabels()) + // nolint: exhaustruct roleBindingsSubjects := []rbacv1.Subject{ { Kind: rbacv1.ServiceAccountKind, @@ -1085,6 +1090,7 @@ func getApiContainerContainersAndVolumes( } containerEnvVars = append(containerEnvVars, ownNamespaceEnvVar) + // nolint: exhaustruct containers := []apiv1.Container{ { Name: kurtosisApiContainerContainerName, diff --git a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go index 42a473b848..5c4a2c97a2 100644 --- a/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go +++ b/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/user_services_functions/start_user_services.go @@ -3,9 +3,10 @@ package user_services_functions import ( "context" "fmt" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user" "strings" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/consts" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/shared_helpers" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager" @@ -681,7 +682,7 @@ func getUserServicePodContainerSpecs( Limits: resourceLimitsList, Requests: resourceRequestsList, } - + // nolint: exhaustruct containers := []apiv1.Container{ { Name: userServiceContainerName, @@ -871,6 +872,7 @@ func createRegisterUserServiceOperation( // Kubernetes doesn't allow us to create services without any ports, so we need to set this to a notional value // until the user calls StartService + // nolint: exhaustruct notionalServicePorts := []apiv1.ServicePort{ { Name: unboundPortName, diff --git a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go index 5a695e7537..c96e23449e 100644 --- a/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_impls/metrics_reporting/metrics_reporting_kurtosis_backend.go @@ -2,11 +2,13 @@ package metrics_reporting import ( "context" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "io" "time" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/api_container" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/compute_resources" @@ -470,3 +472,7 @@ func (backend *MetricsReportingKurtosisBackend) GetAvailableCPUAndMemory(ctx con func (backend *MetricsReportingKurtosisBackend) BuildImage(ctx context.Context, imageName string, imageBuildSpec *image_build_spec.ImageBuildSpec) (string, error) { return backend.underlying.BuildImage(ctx, imageName, imageBuildSpec) } + +func (backend *MetricsReportingKurtosisBackend) NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) { + return backend.underlying.NixBuild(ctx, nixBuildSpec) +} diff --git a/container-engine-lib/lib/backend_interface/kurtosis_backend.go b/container-engine-lib/lib/backend_interface/kurtosis_backend.go index be9a65024f..71b3c16394 100644 --- a/container-engine-lib/lib/backend_interface/kurtosis_backend.go +++ b/container-engine-lib/lib/backend_interface/kurtosis_backend.go @@ -2,11 +2,13 @@ package backend_interface import ( "context" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "io" "time" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/api_container" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/compute_resources" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/enclave" @@ -356,4 +358,6 @@ type KurtosisBackend interface { // BuildImage builds a container image based on the [imageBuildSpec] with [imageName] // Returns image architecture and if error occurred BuildImage(ctx context.Context, imageName string, imageBuildSpec *image_build_spec.ImageBuildSpec) (string, error) + + NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) } diff --git a/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go b/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go index 62e2d0eb5f..63260d872b 100644 --- a/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go +++ b/container-engine-lib/lib/backend_interface/mock_kurtosis_backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.36.0. DO NOT EDIT. package backend_interface @@ -28,6 +28,8 @@ import ( mock "github.com/stretchr/testify/mock" + nix_build_spec "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" + reverse_proxy "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/reverse_proxy" service "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" @@ -152,10 +154,6 @@ func (_c *MockKurtosisBackend_CopyFilesFromUserService_Call) RunAndReturn(run fu func (_m *MockKurtosisBackend) CreateAPIContainer(ctx context.Context, image string, enclaveUuid enclave.EnclaveUUID, grpcPortNum uint16, enclaveDataVolumeDirpath string, ownIpAddressEnvVar string, customEnvVars map[string]string, shouldStartInDebugMode bool) (*api_container.APIContainer, error) { ret := _m.Called(ctx, image, enclaveUuid, grpcPortNum, enclaveDataVolumeDirpath, ownIpAddressEnvVar, customEnvVars, shouldStartInDebugMode) - if len(ret) == 0 { - panic("no return value specified for CreateAPIContainer") - } - var r0 *api_container.APIContainer var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, enclave.EnclaveUUID, uint16, string, string, map[string]string, bool) (*api_container.APIContainer, error)); ok { @@ -1631,6 +1629,59 @@ func (_c *MockKurtosisBackend_GetUserServices_Call) RunAndReturn(run func(contex return _c } +// NixBuild provides a mock function with given fields: ctx, nixBuildSpec +func (_m *MockKurtosisBackend) NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) { + ret := _m.Called(ctx, nixBuildSpec) + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *nix_build_spec.NixBuildSpec) (string, error)); ok { + return rf(ctx, nixBuildSpec) + } + if rf, ok := ret.Get(0).(func(context.Context, *nix_build_spec.NixBuildSpec) string); ok { + r0 = rf(ctx, nixBuildSpec) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, *nix_build_spec.NixBuildSpec) error); ok { + r1 = rf(ctx, nixBuildSpec) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockKurtosisBackend_NixBuild_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NixBuild' +type MockKurtosisBackend_NixBuild_Call struct { + *mock.Call +} + +// NixBuild is a helper method to define mock.On call +// - ctx context.Context +// - nixBuildSpec *nix_build_spec.NixBuildSpec +func (_e *MockKurtosisBackend_Expecter) NixBuild(ctx interface{}, nixBuildSpec interface{}) *MockKurtosisBackend_NixBuild_Call { + return &MockKurtosisBackend_NixBuild_Call{Call: _e.mock.On("NixBuild", ctx, nixBuildSpec)} +} + +func (_c *MockKurtosisBackend_NixBuild_Call) Run(run func(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec)) *MockKurtosisBackend_NixBuild_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*nix_build_spec.NixBuildSpec)) + }) + return _c +} + +func (_c *MockKurtosisBackend_NixBuild_Call) Return(_a0 string, _a1 error) *MockKurtosisBackend_NixBuild_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockKurtosisBackend_NixBuild_Call) RunAndReturn(run func(context.Context, *nix_build_spec.NixBuildSpec) (string, error)) *MockKurtosisBackend_NixBuild_Call { + _c.Call.Return(run) + return _c +} + // PruneUnusedImages provides a mock function with given fields: ctx func (_m *MockKurtosisBackend) PruneUnusedImages(ctx context.Context) ([]string, error) { ret := _m.Called(ctx) @@ -2378,13 +2429,12 @@ func (_c *MockKurtosisBackend_UpdateEnclave_Call) RunAndReturn(run func(context. return _c } -type mockConstructorTestingTNewMockKurtosisBackend interface { +// NewMockKurtosisBackend creates a new instance of MockKurtosisBackend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockKurtosisBackend(t interface { mock.TestingT Cleanup(func()) -} - -// NewMockKurtosisBackend creates a new instance of MockKurtosisBackend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMockKurtosisBackend(t mockConstructorTestingTNewMockKurtosisBackend) *MockKurtosisBackend { +}) *MockKurtosisBackend { mock := &MockKurtosisBackend{} mock.Mock.Test(t) diff --git a/container-engine-lib/lib/backend_interface/objects/nix_build_spec/nix_build_spec.go b/container-engine-lib/lib/backend_interface/objects/nix_build_spec/nix_build_spec.go new file mode 100644 index 0000000000..5de9eafa0b --- /dev/null +++ b/container-engine-lib/lib/backend_interface/objects/nix_build_spec/nix_build_spec.go @@ -0,0 +1,70 @@ +package nix_build_spec + +import ( + "encoding/json" + "fmt" + + "github.com/kurtosis-tech/stacktrace" +) + +type NixBuildSpec struct { + // we do this way in order to have exported fields which can be marshalled + // and an unexported type for encapsulation + privateNixBuildSpec *privateNixBuildSpec +} + +// NixBuildSpec contains the information need for building a container from nix. +type privateNixBuildSpec struct { + NixFlakeDir string + ContextDirPath string + FlakeOutput string + ImageName string +} + +func NewNixBuildSpec(imageName string, contextDirPath string, nixFlakeDir string, flakeOutput string) *NixBuildSpec { + internalNixBuildSpec := &privateNixBuildSpec{ + NixFlakeDir: nixFlakeDir, + ContextDirPath: contextDirPath, + FlakeOutput: flakeOutput, + ImageName: imageName, + } + return &NixBuildSpec{internalNixBuildSpec} +} + +func (nixBuildSpec *NixBuildSpec) GetImageName() string { + return nixBuildSpec.privateNixBuildSpec.ImageName +} + +func (nixBuildSpec *NixBuildSpec) GetNixFlakeDir() string { + return nixBuildSpec.privateNixBuildSpec.NixFlakeDir +} + +func (nixBuildSpec *NixBuildSpec) GetBuildContextDir() string { + return nixBuildSpec.privateNixBuildSpec.ContextDirPath +} + +func (nixBuildSpec *NixBuildSpec) GetFlakeOutput() string { + return nixBuildSpec.privateNixBuildSpec.FlakeOutput +} + +func (nixBuildSpec *NixBuildSpec) GetFullFlakeReference() string { + return fmt.Sprintf("%s/.#%s", nixBuildSpec.privateNixBuildSpec.NixFlakeDir, nixBuildSpec.privateNixBuildSpec.FlakeOutput) +} + +func (nixBuildSpec *NixBuildSpec) MarshalJSON() ([]byte, error) { + return json.Marshal(nixBuildSpec.privateNixBuildSpec) +} + +func (nixBuildSpec *NixBuildSpec) UnmarshalJSON(data []byte) error { + + // Suppressing exhaustruct requirement because we want an object with zero values + // nolint: exhaustruct + unmarshalledPrivateStructPtr := &privateNixBuildSpec{} + + if err := json.Unmarshal(data, unmarshalledPrivateStructPtr); err != nil { + return stacktrace.Propagate(err, "An error occurred unmarshalling the private struct") + } + + nixBuildSpec.privateNixBuildSpec = unmarshalledPrivateStructPtr + return nil +} diff --git a/container-engine-lib/lib/backend_interface/objects/service/service_config.go b/container-engine-lib/lib/backend_interface/objects/service/service_config.go index 44b5daddba..000aec8676 100644 --- a/container-engine-lib/lib/backend_interface/objects/service/service_config.go +++ b/container-engine-lib/lib/backend_interface/objects/service/service_config.go @@ -2,8 +2,10 @@ package service import ( "encoding/json" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user" @@ -30,6 +32,8 @@ type privateServiceConfig struct { // Mutually exclusive from ImageBuildSpec, ContainerImageName ImagerRegistrySpec *image_registry_spec.ImageRegistrySpec + NixBuildSpec *nix_build_spec.NixBuildSpec + PrivatePorts map[string]*port_spec.PortSpec PublicPorts map[string]*port_spec.PortSpec //TODO this is a huge hack to temporarily enable static ports for NEAR until we have a more productized solution @@ -69,6 +73,7 @@ func CreateServiceConfig( containerImageName string, imageBuildSpec *image_build_spec.ImageBuildSpec, imageRegistrySpec *image_registry_spec.ImageRegistrySpec, + nixBuildSpec *nix_build_spec.NixBuildSpec, privatePorts map[string]*port_spec.PortSpec, publicPorts map[string]*port_spec.PortSpec, entrypointArgs []string, @@ -95,6 +100,7 @@ func CreateServiceConfig( ContainerImageName: containerImageName, ImageBuildSpec: imageBuildSpec, ImagerRegistrySpec: imageRegistrySpec, + NixBuildSpec: nixBuildSpec, PrivatePorts: privatePorts, PublicPorts: publicPorts, EntrypointArgs: entrypointArgs, @@ -128,6 +134,10 @@ func (serviceConfig *ServiceConfig) GetImageRegistrySpec() *image_registry_spec. return serviceConfig.privateServiceConfig.ImagerRegistrySpec } +func (serviceConfig *ServiceConfig) GetNixBuildSpec() *nix_build_spec.NixBuildSpec { + return serviceConfig.privateServiceConfig.NixBuildSpec +} + func (serviceConfig *ServiceConfig) GetPrivatePorts() map[string]*port_spec.PortSpec { return serviceConfig.privateServiceConfig.PrivatePorts } diff --git a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go index e40d56b436..3f3df193f8 100644 --- a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go +++ b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go @@ -2,15 +2,17 @@ package service import ( "encoding/json" + "testing" + "time" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" - "testing" - "time" ) func TestServiceConfigMarshallers(t *testing.T) { @@ -66,6 +68,7 @@ func getServiceConfigForTest(t *testing.T, imageName string) *ServiceConfig { imageName, testImageBuildSpec(), testImageRegistrySpec(), + testNixBuildSpec(), testPrivatePorts(t), testPublicPorts(t), []string{"bin", "bash", "ls"}, @@ -187,6 +190,10 @@ func testImageRegistrySpec() *image_registry_spec.ImageRegistrySpec { return image_registry_spec.NewImageRegistrySpec("test-image", "test-userename", "test-password", "test-registry.io") } +func testNixBuildSpec() *nix_build_spec.NixBuildSpec { + return nix_build_spec.NewNixBuildSpec("test-image", "path", "", "") +} + func testServiceUser() *service_user.ServiceUser { su := service_user.NewServiceUser(100) su.SetGID(100) diff --git a/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go b/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go index 92bf06365a..82311e59ce 100644 --- a/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go +++ b/container-engine-lib/lib/database_accessors/enclave_db/service_registration/repository_test.go @@ -2,6 +2,12 @@ package service_registration import ( "fmt" + "math/rand" + "net" + "os" + "testing" + "time" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/enclave" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" @@ -9,11 +15,6 @@ import ( "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/database_accessors/enclave_db" "github.com/stretchr/testify/require" bolt "go.etcd.io/bbolt" - "math/rand" - "net" - "os" - "testing" - "time" ) const ( @@ -306,6 +307,7 @@ func getServiceConfigForTest(t *testing.T, imageName string) *service.ServiceCon imageName, nil, nil, + nil, testPrivatePorts(t), testPublicPorts(t), []string{"bin", "bash", "ls"}, diff --git a/container-engine-lib/lib/image_utils/image_utils.go b/container-engine-lib/lib/image_utils/image_utils.go new file mode 100644 index 0000000000..5b9224df71 --- /dev/null +++ b/container-engine-lib/lib/image_utils/image_utils.go @@ -0,0 +1,72 @@ +package image_utils + +import ( + "archive/tar" + "compress/gzip" + "encoding/json" + "io" + "os" + + "github.com/kurtosis-tech/stacktrace" +) + +const ( + manifestFileName = "manifest.json" +) + +// ImageManifest represents the structure of the manifest.json file +type ImageManifest struct { + Config string `json:"Config"` + RepoTags []string `json:"RepoTags"` + Layers []string `json:"Layers"` +} + +// GetRepoTags extracts the RepoTags from a Docker image file +func GetRepoTags(imageFilePath string) ([]string, error) { + imageFile, err := os.Open(imageFilePath) + if err != nil { + return nil, stacktrace.Propagate(err, "Fail to open image file %s", imageFilePath) + } + defer imageFile.Close() + + gzipReader, err := gzip.NewReader(imageFile) + if err != nil { + return nil, stacktrace.Propagate(err, "Fail to ungzip image file %s", stacktrace.Propagate(err, "Fail to read the image files")) + } + defer gzipReader.Close() + + tarReader := tar.NewReader(gzipReader) + + var found bool = false + for { + header, err := tarReader.Next() + if err == io.EOF { + break + } + if err != nil { + return nil, stacktrace.Propagate(err, "Fail to read the image files") + } + if header.Name == manifestFileName { + found = true + break + } + } + + if !found { + return nil, stacktrace.NewError("manifest.json not found in the image") + } + + var imageManifest []ImageManifest + jsonDecoder := json.NewDecoder(tarReader) + if err := jsonDecoder.Decode(&imageManifest); err != nil { + return nil, stacktrace.Propagate(err, "Could not parse the manifest.json") + } + + if len(imageManifest) > 1 { + return nil, stacktrace.NewError("Image has more than 1 label/tag, don't know which one to pick: %v", imageManifest) + } else if len(imageManifest) < 1 { + return nil, stacktrace.NewError("Image has no label/tag") + } + + return imageManifest[0].RepoTags, nil +} diff --git a/core/server/Dockerfile b/core/server/Dockerfile index df50957179..cd57de82c6 100644 --- a/core/server/Dockerfile +++ b/core/server/Dockerfile @@ -1,7 +1,8 @@ FROM alpine:3.17 # We need protobut-dev to run protobuf compiler against startosis .proto files -RUN apk update && apk add --no-cache bash protobuf-dev +RUN apk update && apk add --no-cache bash protobuf-dev sudo shadow curl xz +RUN sh <(curl -L https://nixos.org/nix/install) --daemon --yes ARG TARGETARCH diff --git a/core/server/Dockerfile.debug b/core/server/Dockerfile.debug index a8b268dc55..1aceb832e5 100644 --- a/core/server/Dockerfile.debug +++ b/core/server/Dockerfile.debug @@ -1,7 +1,8 @@ FROM alpine:3.19 # We need protobut-dev to run protobuf compiler against startosis .proto files -RUN apk update && apk add --no-cache bash protobuf-dev +RUN apk update && apk add --no-cache bash protobuf-dev sudo shadow curl xz +RUN sh <(curl -L https://nixos.org/nix/install) --daemon --yes # Make sure that you changed the port inside the APIC's code before changing it here EXPOSE 50103 diff --git a/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler.go b/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler.go index 366fbd3eb8..b0cca993e4 100644 --- a/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler.go +++ b/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler.go @@ -3,6 +3,12 @@ package docker_compose_transpiler import ( "errors" "fmt" + "os" + "path" + "sort" + "strconv" + "strings" + "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" "github.com/joho/godotenv" @@ -15,11 +21,6 @@ import ( "github.com/kurtosis-tech/stacktrace" "github.com/sirupsen/logrus" "go.starlark.net/starlark" - "os" - "path" - "sort" - "strconv" - "strings" ) const ( @@ -122,6 +123,7 @@ func convertComposeToStarlarkScript(composeBytes []byte, envVars map[string]stri func convertComposeBytesToComposeStruct(composeBytes []byte, envVars map[string]string) (*types.Project, error) { composeParseConfig := types.ConfigDetails{ //nolint:exhaustruct // Note that we might be able to use the WorkingDir property instead, to parse the entire directory + // nolint: exhaustruct ConfigFiles: []types.ConfigFile{{ Content: composeBytes, }}, diff --git a/core/server/api_container/server/service_network/default_service_network_test.go b/core/server/api_container/server/service_network/default_service_network_test.go index 9456aa35d1..516f3aa651 100644 --- a/core/server/api_container/server/service_network/default_service_network_test.go +++ b/core/server/api_container/server/service_network/default_service_network_test.go @@ -1215,6 +1215,7 @@ func testServiceConfig(t *testing.T, imageName string) *service.ServiceConfig { nil, nil, nil, + nil, 0, 0, "", diff --git a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go index e8a4f6a8ac..5aeae0154e 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go @@ -112,6 +112,7 @@ func KurtosisTypeConstructors() []*starlark.Builtin { starlark.NewBuiltin(service_config.ServiceConfigTypeName, service_config.NewServiceConfigType().CreateBuiltin()), starlark.NewBuiltin(service_config.ReadyConditionTypeName, service_config.NewReadyConditionType().CreateBuiltin()), starlark.NewBuiltin(service_config.ImageBuildSpecTypeName, service_config.NewImageBuildSpecType().CreateBuiltin()), + starlark.NewBuiltin(service_config.NixBuildSpecTypeName, service_config.NewNixBuildSpecType().CreateBuiltin()), starlark.NewBuiltin(service_config.ImageRegistrySpecTypeName, service_config.NewImageRegistrySpec().CreateBuiltin()), starlark.NewBuiltin(service_config.UserTypeName, service_config.NewUserType().CreateBuiltin()), starlark.NewBuiltin(service_config.TolerationTypeName, service_config.NewTolerationType().CreateBuiltin()), diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go index e0a264c518..93c28bfe95 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go @@ -3,6 +3,8 @@ package add_service import ( "context" "fmt" + "time" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network" @@ -17,7 +19,6 @@ import ( "github.com/kurtosis-tech/stacktrace" "github.com/sirupsen/logrus" "go.starlark.net/starlark" - "time" ) const ( @@ -107,6 +108,8 @@ func validateSingleService(validatorEnvironment *startosis_validator.ValidatorEn validatorEnvironment.AppendRequiredImageBuild(serviceConfig.GetContainerImageName(), serviceConfig.GetImageBuildSpec()) } else if serviceConfig.GetImageRegistrySpec() != nil { validatorEnvironment.AppendImageToPullWithAuth(serviceConfig.GetContainerImageName(), serviceConfig.GetImageRegistrySpec()) + } else if serviceConfig.GetNixBuildSpec() != nil { + validatorEnvironment.AppendRequiredNixBuild(serviceConfig.GetContainerImageName(), serviceConfig.GetNixBuildSpec()) } else { validatorEnvironment.AppendRequiredImagePull(serviceConfig.GetContainerImageName()) } @@ -199,6 +202,7 @@ func replaceMagicStrings( serviceConfig.GetContainerImageName(), serviceConfig.GetImageBuildSpec(), serviceConfig.GetImageRegistrySpec(), + serviceConfig.GetNixBuildSpec(), serviceConfig.GetPrivatePorts(), serviceConfig.GetPublicPorts(), entrypoints, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go index 24627af559..042c0bbe17 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared_test.go @@ -2,6 +2,9 @@ package add_service import ( "fmt" + "os" + "testing" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/database_accessors/enclave_db" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_instruction/shared_helpers" @@ -10,8 +13,6 @@ import ( "github.com/stretchr/testify/require" bolt "go.etcd.io/bbolt" "go.starlark.net/starlark" - "os" - "testing" ) const ( @@ -42,6 +43,7 @@ func TestAddServiceShared_EntryPointArgsRuntimeValueAreReplaced(t *testing.T) { nil, nil, nil, + nil, []string{"-- " + runtimeValue}, nil, nil, @@ -89,6 +91,7 @@ func TestAddServiceShared_CmdArgsRuntimeValueAreReplaced(t *testing.T) { nil, nil, nil, + nil, []string{"bash", "-c", "sleep " + runtimeValue}, nil, nil, @@ -136,6 +139,7 @@ func TestAddServiceShared_EnvVarsWithRuntimeValueAreReplaced(t *testing.T) { nil, nil, nil, + nil, map[string]string{ "PORT": runtimeValue, }, @@ -190,6 +194,7 @@ func TestAddServiceShared_ServiceNameWithRuntimeValuesAreReplaced(t *testing.T) nil, nil, nil, + nil, 0, 0, "", diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go index 53119e30c1..2632a5eb6c 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go @@ -3,6 +3,10 @@ package tasks import ( "context" "fmt" + "reflect" + "strings" + "time" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/exec_result" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" @@ -19,9 +23,6 @@ import ( "github.com/sirupsen/logrus" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" - "reflect" - "strings" - "time" ) // shared constants @@ -260,6 +261,7 @@ func getServiceConfig( nil, nil, nil, + nil, // This make sure that the container does not stop as soon as it starts // This only is needed for kubernetes at the moment // TODO: Instead of creating a service and running exec commands diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go index f5d5692864..adb5eb05ac 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_service_framework_test.go @@ -2,9 +2,10 @@ package test_engine import ( "fmt" - "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider" "testing" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/container" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" @@ -36,6 +37,7 @@ func (suite *KurtosisPlanInstructionTestSuite) TestAddService() { testContainerImageName, nil, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go index 8ac6c02305..7912c700a0 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/add_services_framework_test.go @@ -2,13 +2,14 @@ package test_engine import ( "fmt" - "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider" "io" "net/http" "net/url" "strings" "testing" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/container" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" @@ -52,6 +53,7 @@ func (suite *KurtosisPlanInstructionTestSuite) TestAddServices() { testContainerImageName, nil, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, @@ -78,6 +80,7 @@ func (suite *KurtosisPlanInstructionTestSuite) TestAddServices() { testContainerImageName, nil, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/nix_build_spec_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/nix_build_spec_framework_test.go new file mode 100644 index 0000000000..f4c5909dd2 --- /dev/null +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/nix_build_spec_framework_test.go @@ -0,0 +1,67 @@ +package test_engine + +import ( + "fmt" + "testing" + + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/service_config" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" + "github.com/stretchr/testify/require" +) + +type nixBuildSpecTest struct { + *testing.T + + packageContentProvider *startosis_packages.MockPackageContentProvider +} + +func (suite *KurtosisTypeConstructorTestSuite) TestNixBuildSpecTest() { + suite.packageContentProvider.EXPECT(). + GetAbsoluteLocator(testModulePackageId, testModuleMainFileLocator, testBuildContextDir, testNoPackageReplaceOptions). + Times(1). + Return(testBuildContextLocator, nil) + + suite.packageContentProvider.EXPECT(). + GetOnDiskAbsolutePackageFilePath(testNixFlakeLocator). + Times(1). + Return(testOnDiskNixFlakePath, nil) + + suite.packageContentProvider.EXPECT(). + GetOnDiskAbsolutePath(testBuildContextLocator). + Times(1). + Return(testOnDiskContextDirPath, nil) + + suite.run(&nixBuildSpecTest{ + T: suite.T(), + packageContentProvider: suite.packageContentProvider, + }) +} + +func (t *nixBuildSpecTest) GetStarlarkCode() string { + return fmt.Sprintf("%s(%s=%q, %s=%q, %s=%q, %s=%q)", + service_config.NixBuildSpecTypeName, + service_config.FlakeLocationDir, + testNixFlakeLocationDir, + service_config.NixContextAttr, + testNixContextDir, + service_config.NixImageName, + testNixImageName, + service_config.FlakeOutputAttr, + testNixFlakeOutput) +} + +func (t *nixBuildSpecTest) Assert(typeValue builtin_argument.KurtosisValueType) { + nixBuildSpecStarlark, ok := typeValue.(*service_config.NixBuildSpec) + require.True(t, ok) + + nixBuildSpec, err := nixBuildSpecStarlark.ToKurtosisType( + testModuleMainFileLocator, + testModulePackageId, + t.packageContentProvider, + testNoPackageReplaceOptions) + require.Nil(t, err) + require.Equal(t, testOnDiskNixFlakeDir, nixBuildSpec.GetNixFlakeDir()) + require.Equal(t, testOnDiskNixContextDirPath, nixBuildSpec.GetBuildContextDir()) + require.Equal(t, testNixFlakeOutput, nixBuildSpec.GetFlakeOutput()) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go index bf69c89472..64740547f7 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go @@ -2,6 +2,8 @@ package test_engine import ( "fmt" + "testing" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" @@ -10,7 +12,6 @@ import ( "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/service_config" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" "github.com/stretchr/testify/require" - "testing" ) type serviceConfigImageBuildSpecTestCase struct { @@ -71,6 +72,7 @@ func (t *serviceConfigImageBuildSpecTestCase) Assert(typeValue builtin_argument. testContainerImageName, expectedImageBuildSpec, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go index 227d5ef7de..af3fa83ba2 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go @@ -2,6 +2,8 @@ package test_engine import ( "fmt" + "testing" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" @@ -10,7 +12,6 @@ import ( "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/service_config" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" "github.com/stretchr/testify/require" - "testing" ) type serviceConfigImageRegistrySpecTest struct { @@ -61,6 +62,7 @@ func (t *serviceConfigImageRegistrySpecTest) Assert(typeValue builtin_argument.K testContainerImageName, nil, expectedImageRegistrySpec, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go index 6e123bb42f..1199948dc0 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_minimal_framework_test.go @@ -2,6 +2,8 @@ package test_engine import ( "fmt" + "testing" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network" @@ -9,7 +11,6 @@ import ( "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/service_config" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" "github.com/stretchr/testify/require" - "testing" ) type serviceConfigMinimalTestCase struct { @@ -48,6 +49,7 @@ func (t *serviceConfigMinimalTestCase) Assert(typeValue builtin_argument.Kurtosi testContainerImageName, nil, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_toleration_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_toleration_test.go index b932a212ff..09f32a09a5 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_toleration_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_toleration_test.go @@ -2,6 +2,8 @@ package test_engine import ( "fmt" + "testing" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network" @@ -10,7 +12,6 @@ import ( "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" - "testing" ) type serviceConfigTolerationTest struct { @@ -63,6 +64,7 @@ func (t *serviceConfigTolerationTest) Assert(typeValue builtin_argument.Kurtosis testContainerImageName, nil, nil, + nil, map[string]*port_spec.PortSpec{}, map[string]*port_spec.PortSpec{}, nil, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go index 5762674c67..2de6f9ee8a 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/static_constants.go @@ -34,6 +34,15 @@ var ( testOnDiskContextDirPath = "kurtosis-data/test-package" testOnDiskContainerImagePath = "kurtosis-data/test-package/Dockerfile" + testNixContextDir = "./" + testNixImageName = "test-image" + testNixFlakeOutput = "foo" + testNixFlakeLocationDir = "./server/app" + testOnDiskNixContextDirPath = "kurtosis-data/test-package" + testOnDiskNixFlakePath = "kurtosis-data/test-package/server/app/flake.nix" + testOnDiskNixFlakeDir = "kurtosis-data/test-package/server/app" + testNixFlakeLocator = "github.com/kurtosistech/test-package/server/app/flake.nix" + testRegistryAddr = "http://registry.test.io" testRegistryUsername = "kurtosis" testRegistryPassword = "password" diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/nix_build_spec.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/nix_build_spec.go new file mode 100644 index 0000000000..5e82f085e5 --- /dev/null +++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/nix_build_spec.go @@ -0,0 +1,232 @@ +package service_config + +import ( + "fmt" + "path" + "path/filepath" + + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_type_constructor" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_constants" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" + "go.starlark.net/starlark" +) + +const ( + NixBuildSpecTypeName = "NixBuildSpec" + + FlakeLocationDir = "flake_location_dir" + FlakeOutputAttr = "flake_output" + NixContextAttr = "build_context_dir" + NixImageName = "image_name" + + // Currently only supports container nix flakes + defaultNixFlakeFile = "flake.nix" +) + +func NewNixBuildSpecType() *kurtosis_type_constructor.KurtosisTypeConstructor { + return &kurtosis_type_constructor.KurtosisTypeConstructor{ + KurtosisBaseBuiltin: &kurtosis_starlark_framework.KurtosisBaseBuiltin{ + Name: NixBuildSpecTypeName, + Arguments: []*builtin_argument.BuiltinArgument{ + { + Name: FlakeLocationDir, + IsOptional: false, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.NonEmptyString(value, FlakeLocationDir) + }, + }, + { + Name: NixContextAttr, + IsOptional: false, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.NonEmptyString(value, NixContextAttr) + }, + }, + { + Name: NixImageName, + IsOptional: false, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.NonEmptyString(value, NixImageName) + }, + }, + { + Name: FlakeOutputAttr, + IsOptional: true, + ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], + Validator: func(value starlark.Value) *startosis_errors.InterpretationError { + return builtin_argument.NonEmptyString(value, FlakeOutputAttr) + }, + }, + }, + }, + Instantiate: instantiateNixBuildSpec, + } +} + +func instantiateNixBuildSpec(arguments *builtin_argument.ArgumentValuesSet) (builtin_argument.KurtosisValueType, *startosis_errors.InterpretationError) { + kurtosisValueType, err := kurtosis_type_constructor.CreateKurtosisStarlarkTypeDefault(NixBuildSpecTypeName, arguments) + if err != nil { + return nil, err + } + return &NixBuildSpec{ + KurtosisValueTypeDefault: kurtosisValueType, + }, nil +} + +// NixBuildSpec is a starlark.Value that holds all the information for the startosis_engine to initiate an nix build +type NixBuildSpec struct { + *kurtosis_type_constructor.KurtosisValueTypeDefault +} + +func (nixBuildSpec *NixBuildSpec) Copy() (builtin_argument.KurtosisValueType, error) { + copiedValueType, err := nixBuildSpec.KurtosisValueTypeDefault.Copy() + if err != nil { + return nil, err + } + return &NixBuildSpec{ + KurtosisValueTypeDefault: copiedValueType, + }, nil +} + +// Relative locator of build context directory +func (nixBuildSpec *NixBuildSpec) GetBuildContextLocator() (string, *startosis_errors.InterpretationError) { + buildContextLocator, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](nixBuildSpec.KurtosisValueTypeDefault, NixContextAttr) + if interpretationErr != nil { + return "", interpretationErr + } + if !found { + return "", startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", + NixContextAttr, NixBuildSpecTypeName) + } + buildContextLocatorStr := buildContextLocator.GoString() + return buildContextLocatorStr, nil +} + +func (nixBuildSpec *NixBuildSpec) GetFlakeOutput() (string, *startosis_errors.InterpretationError) { + flakeOutput, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](nixBuildSpec.KurtosisValueTypeDefault, FlakeOutputAttr) + if interpretationErr != nil { + return "", interpretationErr + } + if !found { + return "", nil + } + return flakeOutput.GoString(), nil +} + +func (nixBuildSpec *NixBuildSpec) GetImageName() (string, *startosis_errors.InterpretationError) { + imageName, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](nixBuildSpec.KurtosisValueTypeDefault, NixImageName) + if interpretationErr != nil { + return "", interpretationErr + } + if !found { + return "", nil + } + return imageName.GoString(), nil +} + +func (nixBuildSpec *NixBuildSpec) GetFlakeLocationDir() (string, *startosis_errors.InterpretationError) { + flakeDir, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](nixBuildSpec.KurtosisValueTypeDefault, FlakeLocationDir) + if interpretationErr != nil { + return "", interpretationErr + } + if !found { + return "", nil + } + return flakeDir.GoString(), nil +} + +func (nixBuildSpec *NixBuildSpec) GetFullFlakeReference() (string, *startosis_errors.InterpretationError) { + flakeDir, err := nixBuildSpec.GetFlakeLocationDir() + if err != nil { + return "", err + } + flakeAttr, err := nixBuildSpec.GetFlakeOutput() + if err != nil { + return "", err + } + fullLocator := fmt.Sprintf("%s/.#%s", flakeDir, flakeAttr) + return fullLocator, nil +} + +func (nixBuildSpec *NixBuildSpec) ToKurtosisType( + locatorOfModuleInWhichThisBuiltInIsBeingCalled string, + packageId string, + packageContentProvider startosis_packages.PackageContentProvider, + packageReplaceOptions map[string]string) (*nix_build_spec.NixBuildSpec, *startosis_errors.InterpretationError) { + // get locator of context directory (relative or absolute) + buildContextLocator, interpretationErr := nixBuildSpec.GetBuildContextLocator() + if interpretationErr != nil { + return nil, interpretationErr + } + + flakeLocationDir, interpretationErr := nixBuildSpec.GetFlakeLocationDir() + if interpretationErr != nil { + return nil, interpretationErr + } + + buildContextDirPathOnDisk, flakeNixFilePathOnDisk, interpretationErr := getOnDiskNixBuildSpecPaths( + buildContextLocator, + flakeLocationDir, + packageId, + locatorOfModuleInWhichThisBuiltInIsBeingCalled, + packageContentProvider, + packageReplaceOptions) + if interpretationErr != nil { + return nil, interpretationErr + } + + imageName, interpretationErr := nixBuildSpec.GetImageName() + if interpretationErr != nil { + return nil, interpretationErr + } + + flakeOutputStr, interpretationErr := nixBuildSpec.GetFlakeOutput() + if interpretationErr != nil { + return nil, interpretationErr + } + + return nix_build_spec.NewNixBuildSpec(imageName, buildContextDirPathOnDisk, flakeNixFilePathOnDisk, flakeOutputStr), nil +} + +// Returns the filepath of the build context directory and flake nix on APIC based on package info +func getOnDiskNixBuildSpecPaths( + buildContextLocator string, + flakeLocationDir string, + packageId string, + locatorOfModuleInWhichThisBuiltInIsBeingCalled string, + packageContentProvider startosis_packages.PackageContentProvider, + packageReplaceOptions map[string]string) (string, string, *startosis_errors.InterpretationError) { + if packageId == startosis_constants.PackageIdPlaceholderForStandaloneScript { + return "", "", startosis_errors.NewInterpretationError("Cannot use NixBuildSpec in a standalone script; create a package and rerun to use NixBuildSpec.") + } + + // get absolute locator of context directory + contextDirAbsoluteLocator, interpretationErr := packageContentProvider.GetAbsoluteLocator(packageId, locatorOfModuleInWhichThisBuiltInIsBeingCalled, buildContextLocator, packageReplaceOptions) + if interpretationErr != nil { + return "", "", interpretationErr + } + + // get on disk directory path of Dockerfile + flakeNixAbsoluteLocator := path.Join(contextDirAbsoluteLocator, flakeLocationDir, defaultNixFlakeFile) + + flakeNixPathOnDisk, interpretationErr := packageContentProvider.GetOnDiskAbsolutePackageFilePath(flakeNixAbsoluteLocator) + if interpretationErr != nil { + return "", "", interpretationErr + } + + contextDirOnDisk, interpretationErr := packageContentProvider.GetOnDiskAbsolutePath(contextDirAbsoluteLocator) + if interpretationErr != nil { + return "", "", interpretationErr + } + // Assume, that flake nix sits at the same level as context directory to get context dir path on disk + flakeDirOnDisk := filepath.Dir(flakeNixPathOnDisk) + + return contextDirOnDisk, flakeDirOnDisk, nil +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go index fee15b0221..8e014b3608 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go @@ -2,8 +2,12 @@ package service_config import ( "fmt" + "math" + "path" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" @@ -22,8 +26,6 @@ import ( "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" "go.starlark.net/starlark" v1 "k8s.io/api/core/v1" - "math" - "path" ) const ( @@ -251,6 +253,7 @@ func (config *ServiceConfig) ToKurtosisType( var ok bool var imageName string + var maybeNixBuildSpec *nix_build_spec.NixBuildSpec var maybeImageBuildSpec *image_build_spec.ImageBuildSpec var maybeImageRegistrySpec *image_registry_spec.ImageRegistrySpec rawImageAttrValue, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.Value](config.KurtosisValueTypeDefault, ImageAttr) @@ -260,7 +263,7 @@ func (config *ServiceConfig) ToKurtosisType( if !found { return nil, startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", ImageAttr, ServiceConfigTypeName) } - imageName, maybeImageBuildSpec, maybeImageRegistrySpec, interpretationErr = convertImage( + imageName, maybeImageBuildSpec, maybeImageRegistrySpec, maybeNixBuildSpec, interpretationErr = convertImage( rawImageAttrValue, locatorOfModuleInWhichThisBuiltInIsBeingCalled, packageId, @@ -502,6 +505,7 @@ func (config *ServiceConfig) ToKurtosisType( imageName, maybeImageBuildSpec, maybeImageRegistrySpec, + maybeNixBuildSpec, privatePorts, publicPorts, entryPointArgs, @@ -673,31 +677,38 @@ func convertImage( locatorOfModuleInWhichThisBuiltInIsBeingCalled string, packageId string, packageContentProvider startosis_packages.PackageContentProvider, - packageReplaceOptions map[string]string) (string, *image_build_spec.ImageBuildSpec, *image_registry_spec.ImageRegistrySpec, *startosis_errors.InterpretationError) { + packageReplaceOptions map[string]string) (string, *image_build_spec.ImageBuildSpec, *image_registry_spec.ImageRegistrySpec, *nix_build_spec.NixBuildSpec, *startosis_errors.InterpretationError) { imageBuildSpecStarlarkType, isImageBuildSpecStarlarkType := image.(*ImageBuildSpec) imageRegistrySpecStarlarkType, isImageRegistrySpecStarlarkType := image.(*ImageRegistrySpec) + nixBuildSpecStarlarkType, isNixBuildSpecStarlarkType := image.(*NixBuildSpec) if isImageBuildSpecStarlarkType { imageBuildSpec, interpretationErr := imageBuildSpecStarlarkType.ToKurtosisType(locatorOfModuleInWhichThisBuiltInIsBeingCalled, packageId, packageContentProvider, packageReplaceOptions) if interpretationErr != nil { - return "", nil, nil, interpretationErr + return "", nil, nil, nil, interpretationErr } imageName, interpretationErr := imageBuildSpecStarlarkType.GetImageName() if interpretationErr != nil { - return "", nil, nil, interpretationErr + return "", nil, nil, nil, interpretationErr } - return imageName, imageBuildSpec, nil, nil + return imageName, imageBuildSpec, nil, nil, nil } else if isImageRegistrySpecStarlarkType { imageRegistrySpec, interpretationErr := imageRegistrySpecStarlarkType.ToKurtosisType() if interpretationErr != nil { - return "", nil, nil, interpretationErr + return "", nil, nil, nil, interpretationErr + } + return imageRegistrySpec.GetImageName(), nil, imageRegistrySpec, nil, nil + } else if isNixBuildSpecStarlarkType { + nixBuildSpec, interpretationErr := nixBuildSpecStarlarkType.ToKurtosisType(locatorOfModuleInWhichThisBuiltInIsBeingCalled, packageId, packageContentProvider, packageReplaceOptions) + if interpretationErr != nil { + return "", nil, nil, nil, interpretationErr } - return imageRegistrySpec.GetImageName(), nil, imageRegistrySpec, nil + return nixBuildSpec.GetImageName(), nil, nil, nixBuildSpec, nil } else { imageName, interpretationErr := kurtosis_types.SafeCastToString(image, ImageAttr) if interpretationErr != nil { - return "", nil, nil, interpretationErr + return "", nil, nil, nil, interpretationErr } - return imageName, nil, nil, nil + return imageName, nil, nil, nil, nil } } diff --git a/core/server/api_container/server/startosis_engine/startosis_validator/images_validator.go b/core/server/api_container/server/startosis_engine/startosis_validator/images_validator.go index 8cc701b9aa..4934fd989f 100644 --- a/core/server/api_container/server/startosis_engine/startosis_validator/images_validator.go +++ b/core/server/api_container/server/startosis_engine/startosis_validator/images_validator.go @@ -2,9 +2,11 @@ package startosis_validator import ( "context" + "sync" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" - "sync" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_download_mode" @@ -58,6 +60,11 @@ func (validator *ImagesValidator) Validate( wg.Add(1) go validator.buildImageUsingBackend(ctx, wg, imageCurrentlyValidating, validator.kurtosisBackend, imageName, imageBuildSpec, imageValidationErrors, imageValidationStarted, imageValidationFinished) } + for imageName, nixBuildSpec := range environment.nixToBuild { + wg.Add(1) + logrus.Warnf("%v - %v", imageName, nixBuildSpec) + go validator.nixBuildUsingBackend(ctx, wg, imageCurrentlyValidating, validator.kurtosisBackend, nixBuildSpec, imageValidationErrors, imageValidationStarted, imageValidationFinished) + } wg.Wait() logrus.Debug("All image validation submitted, currently in progress.") } @@ -116,3 +123,36 @@ func (validator *ImagesValidator) buildImageUsingBackend( } logrus.Debugf("Container image '%s' successfully built", imageName) } + +func (validator *ImagesValidator) nixBuildUsingBackend( + ctx context.Context, + wg *sync.WaitGroup, + imageCurrentlyBuilding chan bool, + backend *backend_interface.KurtosisBackend, + nixBuildSpec *nix_build_spec.NixBuildSpec, + buildErrors chan<- error, + nixBuildStarted chan<- string, + nixBuildFinished chan<- *ValidatedImage) { + imageRef := nixBuildSpec.GetFullFlakeReference() + logrus.Debugf("Requesting the build of image: '%s'", imageRef) + var imageName string + var imageArch string + imageBuiltLocally := true + imagePulledFromRemote := false + defer wg.Done() + imageCurrentlyBuilding <- true + nixBuildStarted <- imageRef + defer func() { + <-imageCurrentlyBuilding + nixBuildFinished <- NewValidatedImage(imageName, imagePulledFromRemote, imageBuiltLocally, imageArch) + }() + + logrus.Debugf("Starting the build of image: '%s'", imageRef) + imageName, err := (*backend).NixBuild(ctx, nixBuildSpec) + if err != nil { + logrus.Warnf("Container image '%s' build failed. Error was: '%s'", imageRef, err.Error()) + buildErrors <- startosis_errors.WrapWithValidationError(err, "Failed to build the required image '%v'.", imageRef) + return + } + logrus.Debugf("Container image '%s' successfully built from Nix definition %s", imageName, imageRef) +} diff --git a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go index b125a0a358..a0968d8457 100644 --- a/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go +++ b/core/server/api_container/server/startosis_engine/startosis_validator/validator_environment.go @@ -5,6 +5,7 @@ import ( "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_download_mode" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors" @@ -15,6 +16,7 @@ import ( type ValidatorEnvironment struct { imagesToPull map[string]*image_registry_spec.ImageRegistrySpec // "set" of images that need to be downloaded imagesToBuild map[string]*image_build_spec.ImageBuildSpec + nixToBuild map[string]*nix_build_spec.NixBuildSpec serviceNames map[service.ServiceName]ComponentExistence artifactNames map[string]ComponentExistence persistentKeys map[service_directory.DirectoryPersistentKey]ComponentExistence @@ -39,6 +41,7 @@ func NewValidatorEnvironment(serviceNames map[service.ServiceName]bool, artifact return &ValidatorEnvironment{ imagesToPull: map[string]*image_registry_spec.ImageRegistrySpec{}, imagesToBuild: map[string]*image_build_spec.ImageBuildSpec{}, + nixToBuild: map[string]*nix_build_spec.NixBuildSpec{}, serviceNames: serviceNamesWithComponentExistence, artifactNames: artifactNamesWithComponentExistence, serviceNameToPrivatePortIDs: serviceNameToPrivatePortIds, @@ -65,8 +68,12 @@ func (environmemt *ValidatorEnvironment) AppendImageToPullWithAuth(containerImag environmemt.imagesToPull[containerImage] = registrySpec } +func (environment *ValidatorEnvironment) AppendRequiredNixBuild(containerImage string, nixBuildSpec *nix_build_spec.NixBuildSpec) { + environment.nixToBuild[containerImage] = nixBuildSpec +} + func (environment *ValidatorEnvironment) GetNumberOfContainerImagesToProcess() uint32 { - return uint32(len(environment.imagesToPull) + len(environment.imagesToBuild)) + return uint32(len(environment.imagesToPull) + len(environment.imagesToBuild) + len(environment.nixToBuild)) } func (environment *ValidatorEnvironment) AddServiceName(serviceName service.ServiceName) { diff --git a/docs/docs/api-reference/starlark-reference/nix-support.md b/docs/docs/api-reference/starlark-reference/nix-support.md new file mode 100644 index 0000000000..19443d7f23 --- /dev/null +++ b/docs/docs/api-reference/starlark-reference/nix-support.md @@ -0,0 +1,52 @@ +--- +title: NixSupport +sidebar_label: NixSupport +--- + +You can provide Kurtosis just with source code and a Nix definition on how to build an image. Kurtosis will take care of building and deploying the image directly into the enclave without the need to upload or register the image beforehand. + +For that, we use Nix flakes, which is a way to package build definitions and dependencies in a reproducible manner. Using Nix flakes, you can define your system configurations and dependencies in a single file (`flake.nix`), making it easier to manage and share. + +Here's a basic explanation of how you can generate Docker images from services using Nix flakes: + +1. **Install Nix**: Installing Nix isn't strictly necessary with Kurtosis, but it's recommended if you are creating or developing the package. You can install it by following the instructions on the Nix website: [https://nixos.org/download.html](https://nixos.org/download.html) + +2. **Create a Nix Flake**: Go to your project root directory and initialize a Nix flake. You can do this by running: + ```bash + cd myproject + nix flake init -t simple + ``` + +3. **Define Your Services**: Inside the `flake.nix` file, you can define your services and their dependencies. For example: + ```nix + { + description = "My project"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { self, nixpkgs, myservice }: { + defaultPackage.aarch64-darwin = nixpkgs.lib.dockerTools.buildImage { + name = "myservice"; + tag = "latest"; + contents = [ myservice ]; + config.Cmd = [ "myservice-binary" ]; + }; + }; + } + ``` + +4. **Add a service definition to Starlark**: Now just add a service to your Starlark configuration: + ```python + plan.add_service( + name = "nix-example", + config = ServiceConfig( + image = NixBuildSpec(image_name = "myservice", flake_location_dir = ".", build_context_dir = "./"), + ), + ) + ``` + +5. **Build and Deploy with Kurtosis**: From your package folder, simply run `kurtosis run .` to get your cluster up and running. + +This is just a basic example. Depending on your specific use case and requirements, you may need to adjust the configuration and dependencies in your `flake.nix` file accordingly. Additionally, you can add more services, configure networking, volumes, environment variables, etc., based on your needs. \ No newline at end of file diff --git a/docs/docs/api-reference/starlark-reference/service-config.md b/docs/docs/api-reference/starlark-reference/service-config.md index 13efc1046d..b2dab77535 100644 --- a/docs/docs/api-reference/starlark-reference/service-config.md +++ b/docs/docs/api-reference/starlark-reference/service-config.md @@ -52,6 +52,26 @@ config = ServiceConfig( # The URL of the registry registry = "http://my.registry.io/" ) + + OR + + image = NixBuildSpec( + # The name of the image that needs to be pulled qualified with the registry + # MANDATORY + image_name = "hello-world-server", + + # Locator to build context within the Kurtosis package + # This allows to select a sub-package where the context is going be used to build the image + # MANDATORY + build_context_dir = "./" + + # The relative path (from the `build_context_dir`) to the folder containing the flake.nix file + # MANDATORY + flake_location_dir = "./hello-go", + + # The selector for the Flake output with the image derivation. Fallbacks to the default package. + flake_output = "containerImage", + ) # The ports that the container should listen on, identified by a user-friendly ID that can be used to select the port again in the future. # If no ports are provided, no ports will be exposed on the host machine, unless there is an EXPOSE in the Dockerfile @@ -194,6 +214,8 @@ The `ports` dictionary argument accepts a key value pair, where `key` is a user The `files` dictionary argument accepts a key value pair, where `key` is the path where the contents of the artifact will be mounted to and `value` is a [Directory][directory] object or files artifact name. Using a `Directory` object with `artifact_name` is strictly equivalent to directly using the files artifact name as the value of the dictionary. This is just to simplify usage. +See [Nix Support][nix-support] for more information on how to use the Nix and Kurtosis together. + You can view more information on [configuring the `ReadyCondition` type here][ready-condition]. :::tip @@ -245,3 +267,4 @@ The `tolerations` field expects a list of [`Toleration`][toleration] objects bei [package]: ../../advanced-concepts/packages.md [user]: ./user.md [toleration]: ./toleration.md +[nix-support]: ./nix-support.md diff --git a/enclave-manager/server/server.go b/enclave-manager/server/server.go index 3b4dabb8ff..5952e7a188 100644 --- a/enclave-manager/server/server.go +++ b/enclave-manager/server/server.go @@ -204,6 +204,7 @@ func (c *WebServer) ListFilesArtifactNamesAndUuids(ctx context.Context, req *con return nil, stacktrace.Propagate(err, "Failed to create the APIC client") } + // nolint: exhaustruct serviceRequest := &connect.Request[emptypb.Empty]{} result, err := (*apiContainerServiceClient).ListFilesArtifactNamesAndUuids(ctx, serviceRequest) if err != nil { @@ -291,6 +292,7 @@ func (c *WebServer) DestroyEnclave(ctx context.Context, req *connect.Request[kur if err != nil { return nil, err } + // nolint: exhaustruct return &connect.Response[emptypb.Empty]{}, nil } diff --git a/engine/server/engine/streaming/websocket_pump.go b/engine/server/engine/streaming/websocket_pump.go index ede4a7efa1..f9d89d9121 100644 --- a/engine/server/engine/streaming/websocket_pump.go +++ b/engine/server/engine/streaming/websocket_pump.go @@ -52,6 +52,7 @@ func NewWebsocketPump[T interface{}](ctx echo.Context, cors cors.Cors) (*Websock ctxWithCancel, cancelFunc := context.WithCancel(context.Background()) + // nolint: exhaustruct pump := &WebsocketPump[T]{ websocket: conn, inputChan: make(chan *T), diff --git a/flake.nix b/flake.nix index 44d25796ac..ef4d08fceb 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,7 @@ golangci-lint delve enumer + go-mockery nodejs_20 node2nix yarn From 5dcdd9e00c0e4f243d2b2e45e752a8c3483cec97 Mon Sep 17 00:00:00 2001 From: Ben Gazzard Date: Fri, 9 Feb 2024 22:47:37 +0000 Subject: [PATCH 049/102] feat: enforce enclave builder validation (#2144) ## Description: This pr is a minor improvement to the enclave builder - it now blocks running until all nodes have valid data. ### Screenshot ![image](https://github.com/kurtosis-tech/kurtosis/assets/4419574/6bf11be2-e606-4ee4-a521-d69abb045797) ## Is this change user facing? YES (experimental) --- .../components/modals/EnclaveBuilderModal.tsx | 126 ++++++++++++++---- .../enclaveBuilder/KurtosisServiceNode.tsx | 9 ++ .../enclaveBuilder/modals/NewFileModal.tsx | 14 +- .../components/src/KurtosisAlertModal.tsx | 13 +- 4 files changed, 132 insertions(+), 30 deletions(-) diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/EnclaveBuilderModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/EnclaveBuilderModal.tsx index 2a66da3532..5947f2c1db 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/EnclaveBuilderModal.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/EnclaveBuilderModal.tsx @@ -3,6 +3,7 @@ import { Button, ButtonGroup, Flex, + ListItem, Modal, ModalBody, ModalCloseButton, @@ -10,9 +11,12 @@ import { ModalFooter, ModalHeader, ModalOverlay, + Text, + Tooltip, + UnorderedList, } from "@chakra-ui/react"; import Dagre from "@dagrejs/dagre"; -import { isDefined, KurtosisAlert, RemoveFunctions, stringifyError } from "kurtosis-ui-components"; +import { isDefined, KurtosisAlert, KurtosisAlertModal, RemoveFunctions, stringifyError } from "kurtosis-ui-components"; import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"; import { FiPlusCircle } from "react-icons/fi"; import { useNavigate } from "react-router-dom"; @@ -53,13 +57,9 @@ type EnclaveBuilderModalProps = { existingEnclave?: RemoveFunctions; }; -export const EnclaveBuilderModal = ({ isOpen, onClose, existingEnclave }: EnclaveBuilderModalProps) => { - const navigator = useNavigate(); - const visualiserRef = useRef(null); - const { createEnclave, runStarlarkScript } = useEnclavesContext(); - const [isLoading, setIsLoading] = useState(false); +export const EnclaveBuilderModal = (props: EnclaveBuilderModalProps) => { + const variableContextKey = useRef(0); const [error, setError] = useState(); - const [currentStarlarkPreview, setCurrentStarlarkPreview] = useState(); const { nodes: initialNodes, @@ -70,7 +70,7 @@ export const EnclaveBuilderModal = ({ isOpen, onClose, existingEnclave }: Enclav edges: Edge[]; data: Record; } => { - const parseResult = getInitialGraphStateFromEnclave(existingEnclave); + const parseResult = getInitialGraphStateFromEnclave(props.existingEnclave); if (parseResult.isErr) { setError(parseResult.error); return { nodes: [], edges: [], data: {} }; @@ -81,7 +81,65 @@ export const EnclaveBuilderModal = ({ isOpen, onClose, existingEnclave }: Enclav .filter(([id, data]) => parseResult.value.nodes.some((node) => node.id === id)) .reduce((acc, [id, data]) => ({ ...acc, [id]: data }), {} as Record), }; - }, [existingEnclave]); + }, [props.existingEnclave]); + + useEffect(() => { + if (!props.isOpen) { + variableContextKey.current += 1; + } + }, [props.isOpen]); + + if (isDefined(error)) { + return ( + { + setError(undefined); + props.onClose(); + }} + /> + ); + } + + return ( + + + + ); +}; + +type EnclaveBuilderModalImplProps = EnclaveBuilderModalProps & { + initialNodes: Node[]; + initialEdges: Edge[]; +}; +const EnclaveBuilderModalImpl = ({ + isOpen, + onClose, + existingEnclave, + initialNodes, + initialEdges, +}: EnclaveBuilderModalImplProps) => { + const navigator = useNavigate(); + const visualiserRef = useRef(null); + const { createEnclave, runStarlarkScript } = useEnclavesContext(); + const { data } = useVariableContext(); + const dataIssues = useMemo( + () => + Object.values(data) + .filter((nodeData) => !nodeData.isValid) + .map( + (nodeData) => + `${nodeData.type} ${ + (nodeData.type === "artifact" ? nodeData.artifactName : nodeData.serviceName) || "with no name" + } has invalid data`, + ), + [data], + ); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(); + const [currentStarlarkPreview, setCurrentStarlarkPreview] = useState(); const handleRun = async () => { if (!isDefined(visualiserRef.current)) { @@ -137,16 +195,14 @@ export const EnclaveBuilderModal = ({ isOpen, onClose, existingEnclave }: Enclav {isDefined(error) && } - - - - - + + + @@ -154,9 +210,31 @@ export const EnclaveBuilderModal = ({ isOpen, onClose, existingEnclave }: Enclav Close - + + + There are data issues that must be addressed before this enclave can run: + + {dataIssues.map((issue, i) => ( + {issue} + ))} + + + ) + } + > + + @@ -194,6 +272,8 @@ const getLayoutedElements = (nodes: Node[], edges: Edge string; }; @@ -212,8 +292,6 @@ const Visualiser = forwardRef( const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes || []); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges || []); - const nodeTypes = useMemo(() => ({ serviceNode: KurtosisServiceNode, artifactNode: KurtosisArtifactNode }), []); - const onLayout = useCallback(() => { const layouted = getLayoutedElements(nodes, edges); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx index 6c16611d97..303d603787 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx @@ -185,6 +185,14 @@ export const KurtosisServiceNode = memo( size={"sm"} placeholder={"Application Protocol (eg postgresql)"} name={`${props.name as `ports.${number}`}.applicationProtocol`} + validate={(val) => { + if (typeof val !== "string") { + return "Value should be a string"; + } + if (val.includes(" ")) { + return "Application protocol cannot include spaces"; + } + }} /> @@ -212,6 +220,7 @@ export const KurtosisServiceNode = memo( /> + name={"files"} diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/NewFileModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/NewFileModal.tsx index db206cc665..eb498a75e5 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/NewFileModal.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/modals/NewFileModal.tsx @@ -9,6 +9,7 @@ import { ModalHeader, ModalOverlay, } from "@chakra-ui/react"; +import { isDefined } from "kurtosis-ui-components"; import { FormProvider, useForm } from "react-hook-form"; import { KurtosisFormControl } from "../../../form/KurtosisFormControl"; import { StringArgumentInput } from "../../../form/StringArgumentInput"; @@ -41,7 +42,18 @@ export const NewFileModal = ({ isOpen, onClose, onConfirm }: NewFileModalProps) helperText={"Enter the full file name for this file (including its path)"} isRequired > - + { + if (!isDefined(v)) { + return "input must be defined"; + } + if (!v.startsWith("/")) { + return "File paths must start with a /"; + } + }} + /> diff --git a/enclave-manager/web/packages/components/src/KurtosisAlertModal.tsx b/enclave-manager/web/packages/components/src/KurtosisAlertModal.tsx index f3ca03f0e7..13b1106e51 100644 --- a/enclave-manager/web/packages/components/src/KurtosisAlertModal.tsx +++ b/enclave-manager/web/packages/components/src/KurtosisAlertModal.tsx @@ -11,6 +11,7 @@ import { ModalOverlay, Text, } from "@chakra-ui/react"; +import { isDefined } from "./utils"; type KurtosisAlertModalProps = { title: string; @@ -18,8 +19,8 @@ type KurtosisAlertModalProps = { isOpen: boolean; isLoading?: boolean; onClose: () => void; - onConfirm: () => void; - confirmText: string; + onConfirm?: () => void; + confirmText?: string; confirmButtonProps?: ButtonProps; }; @@ -47,9 +48,11 @@ export const KurtosisAlertModal = ({ - + {isDefined(onConfirm) && ( + + )} From d2cac109d82fff99140a155ad4848aeb11cfbd1a Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Mon, 12 Feb 2024 06:11:01 -0300 Subject: [PATCH 050/102] chore(main): release 0.86.19 (#2145) :robot: I have created a release *beep* *boop* --- ## [0.86.19](https://github.com/kurtosis-tech/kurtosis/compare/0.86.18...0.86.19) (2024-02-09) ### Features * build nix image ([#2132](https://github.com/kurtosis-tech/kurtosis/issues/2132)) ([0eae9fc](https://github.com/kurtosis-tech/kurtosis/commit/0eae9fc942605f14a04554752a0c0dca7b02b1f7)) * enclave builder tweaks ([#2142](https://github.com/kurtosis-tech/kurtosis/issues/2142)) ([aaf64ca](https://github.com/kurtosis-tech/kurtosis/commit/aaf64ca6085b13b8d70c15114128455277b98e31)) * enforce enclave builder validation ([#2144](https://github.com/kurtosis-tech/kurtosis/issues/2144)) ([5dcdd9e](https://github.com/kurtosis-tech/kurtosis/commit/5dcdd9e00c0e4f243d2b2e45e752a8c3483cec97)) ### Bug Fixes * emui yaml editor populating with values ([#2079](https://github.com/kurtosis-tech/kurtosis/issues/2079)) ([9bd26a8](https://github.com/kurtosis-tech/kurtosis/commit/9bd26a8f855a4c2ac7d5fb2f73c756079f9735a9)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 14 ++++++++++++++ LICENSE.md | 2 +- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- .../src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- .../web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 24 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c4a062cb0..14c8427bfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [0.86.19](https://github.com/kurtosis-tech/kurtosis/compare/0.86.18...0.86.19) (2024-02-09) + + +### Features + +* build nix image ([#2132](https://github.com/kurtosis-tech/kurtosis/issues/2132)) ([0eae9fc](https://github.com/kurtosis-tech/kurtosis/commit/0eae9fc942605f14a04554752a0c0dca7b02b1f7)) +* enclave builder tweaks ([#2142](https://github.com/kurtosis-tech/kurtosis/issues/2142)) ([aaf64ca](https://github.com/kurtosis-tech/kurtosis/commit/aaf64ca6085b13b8d70c15114128455277b98e31)) +* enforce enclave builder validation ([#2144](https://github.com/kurtosis-tech/kurtosis/issues/2144)) ([5dcdd9e](https://github.com/kurtosis-tech/kurtosis/commit/5dcdd9e00c0e4f243d2b2e45e752a8c3483cec97)) + + +### Bug Fixes + +* emui yaml editor populating with values ([#2079](https://github.com/kurtosis-tech/kurtosis/issues/2079)) ([9bd26a8](https://github.com/kurtosis-tech/kurtosis/commit/9bd26a8f855a4c2ac7d5fb2f73c756079f9735a9)) + ## [0.86.18](https://github.com/kurtosis-tech/kurtosis/compare/0.86.17...0.86.18) (2024-02-09) diff --git a/LICENSE.md b/LICENSE.md index 1693a875c0..40946a206a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.18 +Licensed Work: Kurtosis 0.86.19 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index f870cac450..7ff326bd39 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.18" + KurtosisVersion = "0.86.19" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 1581d12f22..ee1dab1ca3 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.18" +version = "0.86.19" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index 597cb6c063..17bba4117c 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.18", + "version": "0.86.19", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index 52b851d843..e467c095ff 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.18" +export const KURTOSIS_VERSION: string = "0.86.19" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index f181f8f0fd..ab4b306ef9 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.18", + "version": "0.86.19", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index a833c6ef80..9a579db5fe 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.18", + "version": "0.86.19", "private": true, "homepage": ".", "dependencies": { @@ -10,7 +10,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.18", + "kurtosis-ui-components": "0.86.19", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "react-mentions": "^4.4.10", diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index 694f8a22e4..5489cb0d28 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.18", + "version": "0.86.19", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 7ba9c1f388..4c134a2757 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.18 +0.86.19 From 431396fbd8059b873100309604aa1db5640a6d74 Mon Sep 17 00:00:00 2001 From: Tedi Mitiku Date: Mon, 12 Feb 2024 13:02:06 -0500 Subject: [PATCH 051/102] docs: add port wait info to service config docs (#2151) ## Description: Received feedback that it's not clear port ready checks are performed on service ports. While the info exists in `PortSpec` docs, this adds the info to `ServiceConfig` which is the most commonly referenced doc for new users. ## Is this change user facing? YES --- .../docs/api-reference/starlark-reference/service-config.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/docs/api-reference/starlark-reference/service-config.md b/docs/docs/api-reference/starlark-reference/service-config.md index b2dab77535..11afaa6592 100644 --- a/docs/docs/api-reference/starlark-reference/service-config.md +++ b/docs/docs/api-reference/starlark-reference/service-config.md @@ -73,8 +73,10 @@ config = ServiceConfig( flake_output = "containerImage", ) - # The ports that the container should listen on, identified by a user-friendly ID that can be used to select the port again in the future. - # If no ports are provided, no ports will be exposed on the host machine, unless there is an EXPOSE in the Dockerfile + # The ports that the container should listen on, identified by a user-friendly ID that can be used to select the port again in the future. + # Kurtosis will automatically perform a check to ensure all declared UDP and TCP ports are open and ready for traffic and connections upon startup. + # You may specify a custom wait timeout duration or disable the feature entirely, learn more via PortSpec docs + # If no ports are provided, no ports will be exposed on the host machine, unless there is an EXPOSE in the Dockerfile. # OPTIONAL (Default: {}) ports = { "grpc": PortSpec( From cea7951478c5c13b39771ac9e66440605b427d71 Mon Sep 17 00:00:00 2001 From: Daniel Park <143957627+chunha-park@users.noreply.github.com> Date: Mon, 12 Feb 2024 16:30:50 -0600 Subject: [PATCH 052/102] docs: Update README.md (#2152) Some small wording changes in up front for readability ## Description: ## Is this change user facing? YES/NO ## References (if applicable): --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7b6da21925..668ac785dc 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,12 @@ What is Kurtosis? ================= -[Kurtosis](https://www.kurtosis.com) is a platform for packaging and launching environments of containerized services ("distributed applications") with a focus on approachability for the average developer. What Docker did for shipping binaries, Kurtosis aims to do even better for distributed applications. +[Kurtosis](https://www.kurtosis.com) handles the complexity of your backend infrastructure so you can focus on building. Think Vercel-like experience for your backend. We’re actively learning more on how best to make that happen. If you are on board with that mission, consider giving us a star ⭐! Kurtosis is formed of: - -- A language for declaring a distributed application in Python syntax ([Starlark](https://github.com/google/starlark-go/blob/master/doc/spec.md)) -- A packaging system for sharing and reusing distributed application components -- A runtime that makes a Kurtosis app Just Work, independent of whether it's running on Docker or Kubernetes, local or in the cloud +- A Pythonic language for defining your application (Starlark) +- A packaging system for sharing and running your distributed application +- A runtime that makes your package Just Work, locally or on the cloud - A set of tools to ease common distributed app development needs (e.g. a log aggregator to ease log-diving, automatic port-forwarding to ease connectivity, a `kurtosis service shell` command to ease container filesystem exploration, etc.) Why should I use Kurtosis? From 5b606b5fcdc25110b055c72b94340598135f349b Mon Sep 17 00:00:00 2001 From: Galen Marchetti Date: Tue, 13 Feb 2024 17:22:16 -0600 Subject: [PATCH 053/102] docs: update README.md for tighter messaging on current kurtosis (#2155) ## Description: Emphasizing differentiation over dcompose and helm at the feature level ## Is this change user facing? YES --- README.md | 69 +++++++++++++++++-------------------------------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 668ac785dc..15c7cf0806 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,33 @@ What is Kurtosis? ================= -[Kurtosis](https://www.kurtosis.com) handles the complexity of your backend infrastructure so you can focus on building. Think Vercel-like experience for your backend. We’re actively learning more on how best to make that happen. If you are on board with that mission, consider giving us a star ⭐! + +Have you ever tried to build on top of a colleague's work, or contribute to an open source project, just to get stuck on the first steps of spinning up a stack to play with? [Kurtosis](https://www.kurtosis.com) handles the complexity of spinning up ephemeral dev or test stacks so you can focus on developing, not configuring. Kurtosis is formed of: -- A Pythonic language for defining your application (Starlark) -- A packaging system for sharing and running your distributed application -- A runtime that makes your package Just Work, locally or on the cloud -- A set of tools to ease common distributed app development needs (e.g. a log aggregator to ease log-diving, automatic port-forwarding to ease connectivity, a `kurtosis service shell` command to ease container filesystem exploration, etc.) +- A packaging system for distributing backend stack definitions, which can run on docker or on kubernetes +- A runtime with a per-stack file management system for reproducibly initializing the state of your stack +- A set of tools to enable devs to interact with their stacks, like they do on docker or k8s -Why should I use Kurtosis? +How is Kurtosis different than Docker Compose or Helm? ========================== -Kurtosis shines when creating, working with, and destroying self-contained distributed application environments. Currently, our users report this to be most useful when: -- You're developing on your application and you need to rapidly iterate on it -- You want to try someone's containerized service or distributed application without setting up an environment, dependencies, etc. -- You want to spin up your distributed application in ephemeral environments as part of your integration tests -- You want to ad-hoc test your application on a big cloud cluster -- You're the author of a containerized service or distributed application and you want to give your users a one-liner to try it -- You want to get an instance of your application running in the cloud without provisioning or administering a Kubernetes cluster +Kurtosis operates at a level higher than Docker Compose or Helm, and produces stacks running on either of the underlying engines (the Docker engine, or Kubernetes). +Because of this additional layer of abstraction, we are able to introduce several features to improve the experience of spinning up ephemeral stacks: + +- A per-stack file management system that enables portable state initialization for dev or test stacks +- Stack-level parameterizability; users have a powerful and flexible way (beyond messing with env vars) to affect modifications in their stacks +- First-class plug-and-play composability; it's expected for users to import stack definitions into larger stacks, and this experience is optimized +- The ability to get all of the above, but running over _either_ the docker engine or k8s, at your election + +Why use Kurtosis? +========================= -If you're in web3, we have even more specific web3 usecases [here](https://web3.kurtosis.com). +Kurtosis is best for: + +- Reusing the logic in your stack definitions for all of: local dev, scheduled testing in CI, and ad-hoc larger-scale testing on k8s clusters +- Giving other devs a way to spin up your application, and commonly used variations of it, with one-liners, via Kurtosis' packaging and parameterization systems +- Handling complex setup logic in your backend stack, like passing arbitrary data between services as they start up, and enforcing arbitrary wait conditions Check out an introductory demo video here: @@ -66,40 +73,6 @@ To see where we're going with the product, check out the roadmap [here](https:// Got more questions? Drop them in our [Github Discussions](https://github.com/kurtosis-tech/kurtosis/discussions/new?category=q-a) where we, or other community members, can help answer. -Why Kurtosis over Compose, Helm, or Terraform? -============================================== -These tools have been around for over a decade, yet most developers still struggle to build distributed applications. Why? In a sentence: building distributed applications is hard, and these tools still haven't made it easy enough for the average developer. - -Some of our observations: - -- No tool works across the whole software lifecycle: Compose is oriented around quick local environments rather than Prod environments, while Helm and Terraform are the opposite. This often means a dedicated DevOps team handles Prod deployment, leading to the same "throw it across the wall" problem the DevOps movement was founded around. -- Compose, Helm, and Terraform use fully declarative paradigms, making difficult the sequential "first this, then this" logic necessary for many prototyping workflows. -- The inherently declarative nature of all three make [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) difficult, leading to frequent copy-pasting. -- All three tend to leave resources hanging around that the developer needs to manually clean up. -- Compose and Helm favor "run it and see what happens" over validation & error-checking, resulting in debugging time and longer dev cycles. -- A significant percentage of developers don't understand how Docker works, and [most don't understand Kubernetes or Terraform][stackoverflow-2022-developer-survey--other-tools]. - -Here's what our users tell us they like about Kurtosis: - -- **It's understandable:** you write code in Python syntax, and you get your distributed application the other side. Variables and functions keep your code DRY. -- **It's portable:** your application runs with a one-liner independent of where you run it. You can build your application on your local Docker, and in seconds get the same thing on your friend's laptop or a Kubernetes cluster in the cloud. -- **It can handle sequential dependencies:** for example, "first generate these files, then use them when starting a service". -- **It's reliable and reproducible:** Kurtosis started as a testing tool and is built to be safe: deterministic execution order, validation to catch errors before runtime, built-in support for inter-service dependencies and readiness checks, etc. Your distributed app should spin up the same way, every time. -- **It abstracts away complexity while being configurable:** instantiating a distributed application is as simple as calling its function with the parameters you want. For example, instantiating a Postgres server with modified username and password: - - On the CLI... - ```bash - kurtosis run github.com/kurtosis-tech/postgres-package '{"user": "bobmarley", "password": "buffalosoldier"}' - ``` - - Inside an environment definition... - ```python - postgres = import_module("github.com/kurtosis-tech/postgres-package/main.star") - - def run(plan): - postgres.run(plan, user = "bobmarley", password = "buffalosoldier") - ``` - Contributing to Kurtosis ======================== From 99f3d4795050da7bdcc3e4b20a3137b5ebc06164 Mon Sep 17 00:00:00 2001 From: Galen Marchetti Date: Tue, 13 Feb 2024 17:40:39 -0600 Subject: [PATCH 054/102] docs: add in icons and rearrange order (#2156) ## Description: why kurtosis first, then helm and dcompose q ## Is this change user facing? YES --- README.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 15c7cf0806..7a948ab361 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ +[![Follow us on X, formerly Twitter](https://img.shields.io/twitter/follow/KurtosisTech?style=social)](https://twitter.com/Kurtosistech) +[![Number of GitHub stars](https://img.shields.io/github/stars/kurtosis-tech/kurtosis)](https://github.com/kurtosis-tech/kurtosis/stargazers) + ---- What is Kurtosis? @@ -13,17 +16,6 @@ Kurtosis is formed of: - A runtime with a per-stack file management system for reproducibly initializing the state of your stack - A set of tools to enable devs to interact with their stacks, like they do on docker or k8s -How is Kurtosis different than Docker Compose or Helm? -========================== - -Kurtosis operates at a level higher than Docker Compose or Helm, and produces stacks running on either of the underlying engines (the Docker engine, or Kubernetes). -Because of this additional layer of abstraction, we are able to introduce several features to improve the experience of spinning up ephemeral stacks: - -- A per-stack file management system that enables portable state initialization for dev or test stacks -- Stack-level parameterizability; users have a powerful and flexible way (beyond messing with env vars) to affect modifications in their stacks -- First-class plug-and-play composability; it's expected for users to import stack definitions into larger stacks, and this experience is optimized -- The ability to get all of the above, but running over _either_ the docker engine or k8s, at your election - Why use Kurtosis? ========================= @@ -33,9 +25,16 @@ Kurtosis is best for: - Giving other devs a way to spin up your application, and commonly used variations of it, with one-liners, via Kurtosis' packaging and parameterization systems - Handling complex setup logic in your backend stack, like passing arbitrary data between services as they start up, and enforcing arbitrary wait conditions -Check out an introductory demo video here: +How is Kurtosis different than Docker Compose or Helm? +========================== + +Kurtosis operates at a level higher than Docker Compose or Helm, and produces stacks running on either of the underlying engines (the Docker engine, or Kubernetes). +Because of this additional layer of abstraction, we are able to introduce several features to improve the experience of spinning up ephemeral stacks: - +- A per-stack file management system that enables portable state initialization for dev or test stacks +- Stack-level parameterizability; users have a powerful and flexible way (beyond messing with env vars) to affect modifications in their stacks +- First-class plug-and-play composability; it's expected for users to import stack definitions into larger stacks, and this experience is optimized +- The ability to get all of the above, but running over _either_ the docker engine or k8s, at your election How do I get going? =================== From da8b0b909842814e602d5e6d021654f518729291 Mon Sep 17 00:00:00 2001 From: Galen Marchetti Date: Tue, 13 Feb 2024 17:58:04 -0600 Subject: [PATCH 055/102] docs: generalizing the going further section (#2157) ## Description: generalizing going further section ## Is this change user facing? YES --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 7a948ab361..a2a01ce152 100644 --- a/README.md +++ b/README.md @@ -58,11 +58,7 @@ If you have an issue or feature request, we'd love to hear about it through one ### Going further -To try more Kurtosis packages just like this one, check out the [`awesome-kurtosis` repo][awesome-kurtosis] or one of these packages: - -- [Ethereum](https://github.com/kurtosis-tech/ethereum-package): fully functional private Ethereum network in Kurtosis with Flashbots MEV-boost, any EL and CL client combination, and a collection of network monitoring tools. -- [DIVE](https://github.com/HugoByte/DIVE): A CLI + Kurtosis package by [Hugobyte](https://hugobyte.com) for the ICON ecosystem that can spin up EVM, Cosmos, or JVM networks with a bridge between them. -- [NEAR](https://github.com/kurtosis-tech/near-package): A private NEAR network in Kurtosis. +To try more Kurtosis packages just like this one, check out the [`awesome-kurtosis` repo][awesome-kurtosis]! To learn about how to write Kurtosis packages, check out our [quickstart][quickstart-reference]. From f784eaf7a24ae282aa470d22e6a9ad721d04cc05 Mon Sep 17 00:00:00 2001 From: Ben Gazzard Date: Wed, 14 Feb 2024 20:54:21 +0000 Subject: [PATCH 056/102] feat: support `run_sh` and `exec` in enclave builder (#2158) ## Description: This PR adds support for `run_sh` and `exec` operations in the emui enclave builder. The allows users to complete the 'getting started with starlark' flow. ## Is this change user facing? YES (experimental flag) ## References (if applicable): * https://docs.kurtosis.com/api-reference/starlark-reference/plan --- .../api-reference/starlark-reference/plan.md | 4 +- docs/docs/guides/running-docker-compose.md | 2 +- .../components/form/CodeEditorInput.tsx | 43 +++ .../components/form/ListArgumentInput.tsx | 4 +- .../components/modals/EnclaveBuilderModal.tsx | 212 +----------- .../enclaveBuilder/KurtosisArtifactNode.tsx | 124 ++----- .../enclaveBuilder/KurtosisExecNode.tsx | 105 ++++++ .../modals/enclaveBuilder/KurtosisNode.tsx | 141 ++++++++ .../enclaveBuilder/KurtosisServiceNode.tsx | 324 ++++-------------- .../enclaveBuilder/KurtosisShellNode.tsx | 135 ++++++++ .../VariableContextProvider.tsx | 33 +- .../modals/enclaveBuilder/Visualiser.tsx | 260 ++++++++++++++ .../input/MentionStringArgumentInput.tsx | 6 +- .../input/MountArtifactFileInput.tsx | 38 ++ .../input/PortConfigurationInput.tsx | 49 +++ .../modals/enclaveBuilder/input/validators.ts | 48 +++ .../components/modals/enclaveBuilder/types.ts | 62 ++++ .../components/modals/enclaveBuilder/utils.ts | 208 ++++++++--- 18 files changed, 1159 insertions(+), 639 deletions(-) create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/form/CodeEditorInput.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisExecNode.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisNode.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisShellNode.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MountArtifactFileInput.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/PortConfigurationInput.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/validators.ts diff --git a/docs/docs/api-reference/starlark-reference/plan.md b/docs/docs/api-reference/starlark-reference/plan.md index 34d18ee71e..99b570c1ec 100644 --- a/docs/docs/api-reference/starlark-reference/plan.md +++ b/docs/docs/api-reference/starlark-reference/plan.md @@ -557,7 +557,7 @@ The instruction returns a `struct` with [future references][future-references-re ..., config=ServiceConfig( name="service_one", - files={"/src": results.file_artifacts[0]}, # copies the directory task into service_one + files={"/src": result.file_artifacts[0]}, # copies the directory task into service_one ) ) # the path to the file will look like: /src/task/test.txt @@ -565,7 +565,7 @@ The instruction returns a `struct` with [future references][future-references-re ..., config=ServiceConfig( name="service_two", - files={"/src": results.file_artifacts[1]}, # copies the file test.txt into service_two + files={"/src": result.file_artifacts[1]}, # copies the file test.txt into service_two ), ) # the path to the file will look like: /src/test.txt ``` diff --git a/docs/docs/guides/running-docker-compose.md b/docs/docs/guides/running-docker-compose.md index 0561d99d96..a0f5402c13 100644 --- a/docs/docs/guides/running-docker-compose.md +++ b/docs/docs/guides/running-docker-compose.md @@ -73,7 +73,7 @@ kurtosis run . ``` OR using github link: ``` -kurtosis run github.com/awesome-compose/nextcloud-redis-mariadb +kurtosis run github.com/docker/awesome-compose/nextcloud-redis-mariadb ``` Behind the scenes, Kurtosis will interpret your Docker Compose setup as a Kurtosis [package](../get-started/basic-concepts.md#package) and convert it into [starlark](../advanced-concepts/starlark.md) that is executed on an [enclave](../get-started/basic-concepts.md#enclave). The output will look like this: diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/form/CodeEditorInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/CodeEditorInput.tsx new file mode 100644 index 0000000000..1e0bc2815b --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/CodeEditorInput.tsx @@ -0,0 +1,43 @@ +import { CodeEditor } from "kurtosis-ui-components"; +import { Controller } from "react-hook-form"; +import { FieldPath, FieldValues } from "react-hook-form/dist/types"; +import { ControllerRenderProps } from "react-hook-form/dist/types/controller"; + +import { KurtosisFormInputProps } from "./types"; + +type CodeEditorInputProps = KurtosisFormInputProps & { + fileName: string; +}; + +export const CodeEditorInput = (props: CodeEditorInputProps) => { + return ( + } + name={props.name} + defaultValue={"" as any} + rules={{ + required: props.isRequired, + validate: props.validate, + }} + disabled={props.disabled} + /> + ); +}; + +type CodeEditorImplProps< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + field: ControllerRenderProps; + fileName: string; +}; + +const CodeEditorInputImpl = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + field, + fileName, +}: CodeEditorImplProps) => { + return ; +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/form/ListArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/ListArgumentInput.tsx index c71fc4da1e..6fa774def5 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/form/ListArgumentInput.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/ListArgumentInput.tsx @@ -1,14 +1,14 @@ import { Button, ButtonGroup, Flex, useToast } from "@chakra-ui/react"; import { CopyButton, PasteButton, stringifyError } from "kurtosis-ui-components"; -import { ReactElement } from "react"; +import { FC } from "react"; import { useFieldArray, useFormContext } from "react-hook-form"; import { FiDelete, FiPlus } from "react-icons/fi"; import { KurtosisSubtypeFormControl } from "./KurtosisFormControl"; import { KurtosisFormInputProps } from "./types"; type ListArgumentInputProps = KurtosisFormInputProps & { - FieldComponent: (props: KurtosisFormInputProps) => ReactElement; + FieldComponent: FC>; createNewValue: () => object; }; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/EnclaveBuilderModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/EnclaveBuilderModal.tsx index 5947f2c1db..f9a5844698 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/EnclaveBuilderModal.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/EnclaveBuilderModal.tsx @@ -1,5 +1,4 @@ import { - Box, Button, ButtonGroup, Flex, @@ -15,41 +14,18 @@ import { Tooltip, UnorderedList, } from "@chakra-ui/react"; -import Dagre from "@dagrejs/dagre"; import { isDefined, KurtosisAlert, KurtosisAlertModal, RemoveFunctions, stringifyError } from "kurtosis-ui-components"; -import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"; -import { FiPlusCircle } from "react-icons/fi"; +import { useEffect, useMemo, useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { - Background, - BackgroundVariant, - Controls, - Edge, - Node, - ReactFlow, - ReactFlowProvider, - useEdgesState, - useNodesState, - useReactFlow, - XYPosition, -} from "reactflow"; +import { Edge, Node, ReactFlowProvider } from "reactflow"; import "reactflow/dist/style.css"; -import { v4 as uuidv4 } from "uuid"; import { useEnclavesContext } from "../../EnclavesContext"; import { EnclaveFullInfo } from "../../types"; -import { KurtosisArtifactNode } from "./enclaveBuilder/KurtosisArtifactNode"; -import { KurtosisServiceNode } from "./enclaveBuilder/KurtosisServiceNode"; import { ViewStarlarkModal } from "./enclaveBuilder/modals/ViewStarlarkModal"; -import { - generateStarlarkFromGraph, - getInitialGraphStateFromEnclave, - getNodeDependencies, -} from "./enclaveBuilder/utils"; -import { - KurtosisNodeData, - useVariableContext, - VariableContextProvider, -} from "./enclaveBuilder/VariableContextProvider"; +import { KurtosisNodeData } from "./enclaveBuilder/types"; +import { getInitialGraphStateFromEnclave, getNodeName } from "./enclaveBuilder/utils"; +import { useVariableContext, VariableContextProvider } from "./enclaveBuilder/VariableContextProvider"; +import { Visualiser, VisualiserImperativeAttributes } from "./enclaveBuilder/Visualiser"; type EnclaveBuilderModalProps = { isOpen: boolean; @@ -70,6 +46,7 @@ export const EnclaveBuilderModal = (props: EnclaveBuilderModalProps) => { edges: Edge[]; data: Record; } => { + variableContextKey.current += 1; const parseResult = getInitialGraphStateFromEnclave(props.existingEnclave); if (parseResult.isErr) { setError(parseResult.error); @@ -129,12 +106,7 @@ const EnclaveBuilderModalImpl = ({ () => Object.values(data) .filter((nodeData) => !nodeData.isValid) - .map( - (nodeData) => - `${nodeData.type} ${ - (nodeData.type === "artifact" ? nodeData.artifactName : nodeData.serviceName) || "with no name" - } has invalid data`, - ), + .map((nodeData) => `${nodeData.type} ${getNodeName(nodeData)} has invalid data`), [data], ); const [isLoading, setIsLoading] = useState(false); @@ -210,7 +182,6 @@ const EnclaveBuilderModalImpl = ({ Close - ); }; - -const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); - -const getLayoutedElements = (nodes: Node[], edges: Edge[]) => { - if (nodes.length === 0) { - return { nodes, edges }; - } - g.setGraph({ rankdir: "LR", ranksep: 100 }); - - edges.forEach((edge) => g.setEdge(edge.source, edge.target)); - nodes.forEach((node) => - g.setNode(node.id, node as Node<{ label: string }, string | undefined> & { width?: number; height?: number }), - ); - - Dagre.layout(g); - - return { - nodes: nodes.map((node) => { - const { x, y } = g.node(node.id); - - return { ...node, position: { x, y } }; - }), - edges, - }; -}; - -const nodeTypes = { serviceNode: KurtosisServiceNode, artifactNode: KurtosisArtifactNode }; - -type VisualiserImperativeAttributes = { - getStarlark: () => string; -}; - -type VisualiserProps = { - initialNodes: Node[]; - initialEdges: Edge[]; - existingEnclave?: RemoveFunctions; -}; - -const Visualiser = forwardRef( - ({ initialNodes, initialEdges, existingEnclave }, ref) => { - const { data, updateData } = useVariableContext(); - const insertOffset = useRef(0); - const { fitView, addNodes, getViewport } = useReactFlow(); - const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes || []); - const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges || []); - - const onLayout = useCallback(() => { - const layouted = getLayoutedElements(nodes, edges); - - setNodes([...layouted.nodes]); - setEdges([...layouted.edges]); - - window.requestAnimationFrame(() => { - fitView(); - }); - }, [nodes, edges, fitView, setEdges, setNodes]); - - const getNewNodePosition = (): XYPosition => { - const viewport = getViewport(); - insertOffset.current += 1; - return { x: -viewport.x + insertOffset.current * 20 + 400, y: -viewport.y + insertOffset.current * 20 }; - }; - - const handleAddServiceNode = () => { - const id = uuidv4(); - updateData(id, { type: "service", serviceName: "", image: "", ports: [], env: [], files: [], isValid: false }); - addNodes({ - id, - position: getNewNodePosition(), - width: 650, - style: { width: "650px" }, - type: "serviceNode", - data: {}, - }); - }; - - const handleAddArtifactNode = () => { - const id = uuidv4(); - updateData(id, { type: "artifact", artifactName: "", files: {}, isValid: false }); - addNodes({ - id, - position: getNewNodePosition(), - width: 600, - style: { width: "400px" }, - type: "artifactNode", - data: {}, - }); - }; - - useEffect(() => { - setEdges((prevState) => { - return Object.entries(getNodeDependencies(data)).flatMap(([to, froms]) => - [...froms].map((from) => ({ - id: `${from}-${to}`, - source: from, - target: to, - animated: true, - style: { strokeWidth: "3px" }, - })), - ); - }); - }, [setEdges, data]); - - // Remove the resizeObserver error - useEffect(() => { - const errorHandler = (e: any) => { - if ( - e.message.includes( - "ResizeObserver loop completed with undelivered notifications" || "ResizeObserver loop limit exceeded", - ) - ) { - const resizeObserverErr = document.getElementById("webpack-dev-server-client-overlay"); - if (resizeObserverErr) { - resizeObserverErr.style.display = "none"; - } - } - }; - window.addEventListener("error", errorHandler); - - return () => { - window.removeEventListener("error", errorHandler); - }; - }, []); - - useImperativeHandle( - ref, - () => ({ - getStarlark: () => { - return generateStarlarkFromGraph(nodes, edges, data, existingEnclave); - }, - }), - [nodes, edges, data, existingEnclave], - ); - - return ( - - - - - - - - (insertOffset.current = 1)} - onNodesChange={onNodesChange} - onEdgesChange={onEdgesChange} - nodeTypes={nodeTypes} - fitView - > - - - - - - ); - }, -); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx index ea3722fd38..84fb5d78b1 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx @@ -1,109 +1,41 @@ -import { Flex, IconButton, Text } from "@chakra-ui/react"; +import { isDefined } from "kurtosis-ui-components"; import { memo } from "react"; -import { FormProvider, useForm } from "react-hook-form"; -import { FiTrash } from "react-icons/fi"; -import { RxCornerBottomRight } from "react-icons/rx"; -import { Handle, NodeProps, NodeResizeControl, Position, useReactFlow } from "reactflow"; +import { NodeProps } from "reactflow"; import { KurtosisFormControl } from "../../form/KurtosisFormControl"; import { StringArgumentInput } from "../../form/StringArgumentInput"; import { FileTreeArgumentInput } from "./input/FileTreeArgumentInput"; -import { KurtosisArtifactNodeData, useVariableContext } from "./VariableContextProvider"; +import { validateName } from "./input/validators"; +import { KurtosisNode } from "./KurtosisNode"; +import { KurtosisArtifactNodeData } from "./types"; +import { useVariableContext } from "./VariableContextProvider"; export const KurtosisArtifactNode = memo( ({ id, selected }: NodeProps) => { - const { data, updateData, removeData } = useVariableContext(); - const formMethods = useForm({ - defaultValues: (data[id] as KurtosisArtifactNodeData) || {}, - mode: "onBlur", - shouldFocusError: false, - }); + const { data } = useVariableContext(); + const nodeData = data[id] as KurtosisArtifactNodeData; - const { deleteElements } = useReactFlow(); - - const handleDeleteNode = (e: React.MouseEvent) => { - e.stopPropagation(); - e.preventDefault(); - deleteElements({ nodes: [{ id }] }); - removeData(id); - }; - - const handleBlur = async () => { - const isValid = await formMethods.trigger(); - updateData(id, { ...formMethods.getValues(), isValid }); - }; + if (!isDefined(nodeData)) { + // Node has probably been deleted. + return null; + } return ( - - - - - - - - - - - {(data[id] as KurtosisArtifactNodeData)?.artifactName || Unnamed Artifact} - - } - colorScheme={"red"} - variant={"ghost"} - size={"sm"} - onClick={handleDeleteNode} - /> - - - name={"artifactName"} label={"Artifact Name"} isRequired> - - - - - - - - + + name={"artifactName"} label={"Artifact Name"} isRequired> + + + + + + ); }, - (oldProps, newProps) => { - return oldProps.id === newProps.id && oldProps.selected === newProps.selected; - }, + (oldProps, newProps) => oldProps.id !== newProps.id || oldProps.selected !== newProps.selected, ); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisExecNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisExecNode.tsx new file mode 100644 index 0000000000..d24ff1b9f3 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisExecNode.tsx @@ -0,0 +1,105 @@ +import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; +import { isDefined } from "kurtosis-ui-components"; +import { memo, useMemo } from "react"; +import { NodeProps } from "reactflow"; +import { IntegerArgumentInput } from "../../form/IntegerArgumentInput"; +import { KurtosisFormControl } from "../../form/KurtosisFormControl"; +import { ListArgumentInput } from "../../form/ListArgumentInput"; +import { SelectArgumentInput, SelectOption } from "../../form/SelectArgumentInput"; +import { StringArgumentInput } from "../../form/StringArgumentInput"; +import { KurtosisFormInputProps } from "../../form/types"; +import { MentionStringArgumentInput } from "./input/MentionStringArgumentInput"; +import { validateName } from "./input/validators"; +import { KurtosisNode } from "./KurtosisNode"; +import { KurtosisExecNodeData, KurtosisServiceNodeData } from "./types"; +import { useVariableContext } from "./VariableContextProvider"; + +export const KurtosisExecNode = memo( + ({ id, selected }: NodeProps) => { + const { data, variables } = useVariableContext(); + const nodeData = data[id] as KurtosisExecNodeData; + + const serviceVariableOptions = useMemo((): SelectOption[] => { + return variables + .filter((variable) => variable.id.match(/^service\.[^.]+\.name+$/)) + .map((variable) => ({ + display: variable.displayName.replace(/service\.(.*)\.name/, "$1"), + value: `{{${variable.id}}}`, + })); + }, [variables]); + + if (!isDefined(nodeData)) { + // Node has probably been deleted. + return null; + } + + return ( + + name={"execName"} label={"Exec Name"} isRequired> + + + + + Config + Advanced + + + + {" "} + + name={"serviceName"} + label={"Service"} + helperText={"Choose which service to run this command in."} + isRequired + > + + options={serviceVariableOptions} + isRequired + size={"sm"} + placeholder={"Select a Service"} + name={`serviceName`} + /> + + name={"command"} label={"Command"} isRequired> + + + + + + name={"acceptableCodes"} + label={"Acceptable Exit Codes"} + isRequired + > + + FieldComponent={AcceptableCodeInput} + size={"sm"} + name={"acceptableCodes"} + createNewValue={() => ({ value: 0 })} + isRequired + /> + + + + + + ); + }, + (oldProps, newProps) => oldProps.id !== newProps.id || oldProps.selected !== newProps.selected, +); + +const AcceptableCodeInput = (props: KurtosisFormInputProps) => { + return ( + + {...props} + size={"sm"} + name={`${props.name as `acceptableCodes.${number}`}.value`} + /> + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisNode.tsx new file mode 100644 index 0000000000..1a41c7334b --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisNode.tsx @@ -0,0 +1,141 @@ +import { Flex, IconButton, Text, useToken } from "@chakra-ui/react"; +import { debounce } from "lodash"; +import { memo, PropsWithChildren, useEffect, useMemo } from "react"; +import { DefaultValues, FormProvider, useForm } from "react-hook-form"; +import { FiTrash } from "react-icons/fi"; +import { RxCornerBottomRight } from "react-icons/rx"; +import { Handle, NodeResizeControl, Position, useReactFlow } from "reactflow"; +import { KurtosisNodeData } from "./types"; +import { useVariableContext } from "./VariableContextProvider"; + +type KurtosisNodeProps = PropsWithChildren<{ + id: string; + name: string; + selected: boolean; + minWidth: number; + maxWidth: number; + color: string; +}>; + +export const KurtosisNode = memo( + ({ + id, + name, + selected, + minWidth, + maxWidth, + children, + color, + }: KurtosisNodeProps) => { + const chakraColor = useToken("colors", color); + const { data, updateData, removeData } = useVariableContext(); + const formMethods = useForm({ + defaultValues: (data[id] as DefaultValues) || {}, + mode: "onBlur", + shouldFocusError: false, + }); + + const { deleteElements, zoomOut, zoomIn } = useReactFlow(); + + const handleDeleteNode = (e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + deleteElements({ nodes: [{ id }] }); + removeData(id); + }; + + const handleChange = useMemo( + () => + debounce(async () => { + const isValid = await formMethods.trigger(); + updateData(id, { ...formMethods.getValues(), isValid }); + }, 500), + [updateData, formMethods, id], + ); + + useEffect(() => { + const watcher = formMethods.watch(handleChange); + return () => watcher.unsubscribe(); + }, [formMethods, handleChange]); + + const handleScroll = (e: React.WheelEvent) => { + if (e.currentTarget.scrollTop === 0 && e.deltaY < 0) { + zoomIn(); + } + if ( + Math.abs(e.currentTarget.scrollHeight - e.currentTarget.clientHeight - e.currentTarget.scrollTop) <= 1 && + e.deltaY > 0 + ) { + zoomOut(); + } + }; + + return ( + + + + + + + + + + {name || Unnamed} + } + colorScheme={"red"} + variant={"ghost"} + size={"sm"} + onClick={handleDeleteNode} + /> + + + {children} + + + + ); + }, +); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx index 303d603787..5ddb6abb4b 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx @@ -1,270 +1,90 @@ -import { Flex, Grid, GridItem, IconButton, Tab, TabList, TabPanel, TabPanels, Tabs, Text } from "@chakra-ui/react"; -import { memo, useMemo } from "react"; -import { FormProvider, useForm } from "react-hook-form"; -import { FiTrash } from "react-icons/fi"; -import { RxCornerBottomRight } from "react-icons/rx"; -import { Handle, NodeProps, NodeResizeControl, Position, useReactFlow } from "reactflow"; +import { Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; +import { memo } from "react"; +import { NodeProps } from "reactflow"; import { DictArgumentInput } from "../../form/DictArgumentInput"; -import { IntegerArgumentInput } from "../../form/IntegerArgumentInput"; import { KurtosisFormControl } from "../../form/KurtosisFormControl"; import { ListArgumentInput } from "../../form/ListArgumentInput"; -import { OptionsArgumentInput } from "../../form/OptionArgumentInput"; -import { SelectArgumentInput, SelectOption } from "../../form/SelectArgumentInput"; import { StringArgumentInput } from "../../form/StringArgumentInput"; import { MentionStringArgumentInput } from "./input/MentionStringArgumentInput"; -import { - KurtosisFileMount, - KurtosisPort, - KurtosisServiceNodeData, - useVariableContext, -} from "./VariableContextProvider"; +import { MountArtifactFileInput } from "./input/MountArtifactFileInput"; +import { PortConfigurationField } from "./input/PortConfigurationInput"; +import { validateDockerLocator, validateName } from "./input/validators"; +import { KurtosisNode } from "./KurtosisNode"; +import { KurtosisFileMount, KurtosisPort, KurtosisServiceNodeData } from "./types"; +import { useVariableContext } from "./VariableContextProvider"; export const KurtosisServiceNode = memo( ({ id, selected }: NodeProps) => { - const { data, updateData, removeData, variables } = useVariableContext(); - const artifactVariableOptions = useMemo((): SelectOption[] => { - return variables - .filter((variable) => variable.id.startsWith("artifact")) - .map((variable) => ({ display: variable.displayName, value: `{{${variable.id}}}` })); - }, [variables]); - const formMethods = useForm({ - defaultValues: (data[id] as KurtosisServiceNodeData) || {}, - mode: "onBlur", - shouldFocusError: false, - }); - - const { deleteElements } = useReactFlow(); - - const handleDeleteNode = (e: React.MouseEvent) => { - e.stopPropagation(); - e.preventDefault(); - deleteElements({ nodes: [{ id }] }); - removeData(id); - }; - - const handleBlur = async () => { - const isValid = await formMethods.trigger(); - updateData(id, { ...formMethods.getValues(), isValid }); - }; + const { data } = useVariableContext(); return ( - - - - - - - + + + name={"serviceName"} label={"Service Name"} isRequired> + + + name={"image"} label={"Container Image"} isRequired> + + + + + + Environment + Ports + Files + - - - {(data[id] as KurtosisServiceNodeData)?.serviceName || Unnamed Service} - - } - colorScheme={"red"} - variant={"ghost"} - size={"sm"} - onClick={handleDeleteNode} - /> - - - - name={"serviceName"} label={"Service Name"} isRequired> - { - if (typeof val !== "string") { - return "Value should be a string"; - } - if (!val.match(/^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$/)) { - return ( - "Service names must adhere to the RFC 1035 standard, specifically implementing this regex and" + - " be 1-63 characters long: ^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$. This means the service name must " + - "only contain lowercase alphanumeric characters or '-', and must start with a lowercase alphabet " + - "and end with a lowercase alphanumeric" - ); - } - }} + + + name={"env"} label={"Environment Variables"}> + + name={"env"} + KeyFieldComponent={StringArgumentInput} + ValueFieldComponent={MentionStringArgumentInput} /> - name={"image"} label={"Container Image"} isRequired> - { - if (typeof val !== "string") { - return "Value should be a string"; - } - if ( - !val.match( - /^(?[\w.\-_]+((?::\d+|)(?=\/[a-z0-9._-]+\/[a-z0-9._-]+))|)(?:\/|)(?[a-z0-9.\-_]+(?:\/[a-z0-9.\-_]+|))(:(?[\w.\-_]{1,127})|)$/gim, - ) - ) { - return "Value does not look like a docker image"; - } - }} + + + name={"ports"} label={"Ports"}> + ({ + portName: "", + applicationProtocol: "", + transportProtocol: "TCP", + port: 0, + })} /> - - - - Environment Variables - Ports - Files - - - - - name={"env"} label={"Environment Variables"}> - - name={"env"} - KeyFieldComponent={StringArgumentInput} - ValueFieldComponent={MentionStringArgumentInput} - /> - - - - name={"ports"} label={"Ports"}> - ( - - - - {...props} - size={"sm"} - placeholder={"Port Name (eg postgres)"} - name={`${props.name as `ports.${number}`}.portName`} - /> - - - - {...props} - size={"sm"} - placeholder={"Application Protocol (eg postgresql)"} - name={`${props.name as `ports.${number}`}.applicationProtocol`} - validate={(val) => { - if (typeof val !== "string") { - return "Value should be a string"; - } - if (val.includes(" ")) { - return "Application protocol cannot include spaces"; - } - }} - /> - - - - {...props} - options={["TCP", "UDP"]} - name={`${props.name as `ports.${number}`}.transportProtocol`} - /> - - - - {...props} - name={`${props.name as `ports.${number}`}.port`} - size={"sm"} - /> - - - )} - createNewValue={(): KurtosisPort => ({ - portName: "", - applicationProtocol: "", - transportProtocol: "TCP", - port: 0, - })} - /> - - - - - - name={"files"} - label={"Files"} - helperText={"Choose where to mount artifacts on this services filesystem"} - > - ( - - - - {...props} - size={"sm"} - placeholder={"/some/path"} - name={`${props.name as `files.${number}`}.mountPoint`} - /> - - - - options={artifactVariableOptions} - {...props} - size={"sm"} - placeholder={"Select an Artifact"} - name={`${props.name as `files.${number}`}.artifactName`} - /> - - - )} - createNewValue={(): KurtosisFileMount => ({ - mountPoint: "", - artifactName: "", - })} - /> - - - - - - - + + + + name={"files"} + label={"Files"} + helperText={"Choose where to mount artifacts on this services filesystem"} + > + ({ + mountPoint: "", + artifactName: "", + })} + /> + + + + + ); }, - (oldProps, newProps) => { - return oldProps.id === newProps.id && oldProps.selected === newProps.selected; - }, + (oldProps, newProps) => oldProps.id !== newProps.id || oldProps.selected !== newProps.selected, ); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisShellNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisShellNode.tsx new file mode 100644 index 0000000000..f354bb5a6e --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisShellNode.tsx @@ -0,0 +1,135 @@ +import { Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; +import { isDefined } from "kurtosis-ui-components"; +import { memo } from "react"; +import { NodeProps } from "reactflow"; +import { BooleanArgumentInput } from "../../form/BooleanArgumentInput"; +import { CodeEditorInput } from "../../form/CodeEditorInput"; +import { DictArgumentInput } from "../../form/DictArgumentInput"; +import { KurtosisFormControl } from "../../form/KurtosisFormControl"; +import { ListArgumentInput } from "../../form/ListArgumentInput"; +import { StringArgumentInput } from "../../form/StringArgumentInput"; +import { MentionStringArgumentInput } from "./input/MentionStringArgumentInput"; +import { MountArtifactFileInput } from "./input/MountArtifactFileInput"; +import { validateDockerLocator, validateDurationString, validateName } from "./input/validators"; +import { KurtosisNode } from "./KurtosisNode"; +import { KurtosisFileMount, KurtosisShellNodeData } from "./types"; +import { useVariableContext } from "./VariableContextProvider"; + +export const KurtosisShellNode = memo( + ({ id, selected }: NodeProps) => { + const { data } = useVariableContext(); + const nodeData = data[id] as KurtosisShellNodeData; + + if (!isDefined(nodeData)) { + // Node has probably been deleted. + return null; + } + + return ( + + + name={"shellName"} label={"Shell Name"} isRequired> + + + name={"image"} label={"Container Image"}> + + + + + + Script + Environment + Files + Advanced + + + + + name={"command"} label={"Script to run"} isRequired> + + + + + name={"env"} label={"Environment Variables"}> + + name={"env"} + KeyFieldComponent={StringArgumentInput} + ValueFieldComponent={MentionStringArgumentInput} + /> + + + + + name={"files"} + label={"Input Files"} + helperText={"Choose where to mount artifacts on this execution tasks filesystem"} + > + ({ + mountPoint: "", + artifactName: "", + })} + /> + + + name={"store"} + label={"Output File/Directory"} + helperText={ + "Choose which files to expose from this execution task. You can use either an absolute path, a directory, or a glob." + } + isRequired + > + + name={"store"} + placeholder={"/some/output/location"} + isRequired + /> + + + + + + name={"wait_enabled"} + label={"Wait enabled"} + isRequired + helperText={"Whether kurtosis should wait a preset time for this step to complete."} + > + name={"wait_enabled"} /> + + + name={"wait"} + label={"Wait"} + isDisabled={nodeData.wait_enabled === "false"} + helperText={"Whether kurtosis should wait a preset time for this step to complete."} + > + + name={"wait"} + isDisabled={nodeData.wait_enabled === "false"} + size={"sm"} + placeholder={"180s"} + validate={nodeData.wait_enabled === "false" ? undefined : validateDurationString} + /> + + + + + + + ); + }, + (oldProps, newProps) => oldProps.id !== newProps.id || oldProps.selected !== newProps.selected, +); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/VariableContextProvider.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/VariableContextProvider.tsx index d5e3d742cc..f071f4f782 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/VariableContextProvider.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/VariableContextProvider.tsx @@ -1,38 +1,7 @@ import { createContext, PropsWithChildren, useCallback, useContext, useMemo, useState } from "react"; -import { Variable } from "./types"; +import { KurtosisNodeData, Variable } from "./types"; import { getVariablesFromNodes } from "./utils"; -export type KurtosisPort = { - portName: string; - port: number; - transportProtocol: "TCP" | "UDP"; - applicationProtocol: string; -}; - -export type KurtosisFileMount = { - mountPoint: string; - artifactName: string; -}; - -export type KurtosisServiceNodeData = { - type: "service"; - serviceName: string; - image: string; - env: { key: string; value: string }[]; - ports: KurtosisPort[]; - files: KurtosisFileMount[]; - isValid: boolean; -}; - -export type KurtosisArtifactNodeData = { - type: "artifact"; - artifactName: string; - files: Record; - isValid: boolean; -}; - -export type KurtosisNodeData = KurtosisArtifactNodeData | KurtosisServiceNodeData; - type VariableContextState = { data: Record; variables: Variable[]; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.tsx new file mode 100644 index 0000000000..ece3844b1b --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.tsx @@ -0,0 +1,260 @@ +import { Box, Button, ButtonGroup, Flex } from "@chakra-ui/react"; +import Dagre from "@dagrejs/dagre"; +import { RemoveFunctions } from "kurtosis-ui-components"; +import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef } from "react"; +import { FiPlusCircle } from "react-icons/fi"; +import { + Background, + BackgroundVariant, + Controls, + Edge, + Node, + ReactFlow, + useEdgesState, + useNodesState, + useReactFlow, + XYPosition, +} from "reactflow"; +import { v4 as uuidv4 } from "uuid"; +import { EnclaveFullInfo } from "../../../types"; +import { KurtosisArtifactNode } from "./KurtosisArtifactNode"; +import { KurtosisExecNode } from "./KurtosisExecNode"; +import { KurtosisServiceNode } from "./KurtosisServiceNode"; +import { KurtosisShellNode } from "./KurtosisShellNode"; +import { generateStarlarkFromGraph, getNodeDependencies } from "./utils"; +import { useVariableContext } from "./VariableContextProvider"; + +const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); +const getLayoutedElements = (nodes: Node[], edges: Edge[]) => { + if (nodes.length === 0) { + return { nodes, edges }; + } + g.setGraph({ rankdir: "LR", ranksep: 100 }); + + edges.forEach((edge) => g.setEdge(edge.source, edge.target)); + nodes.forEach((node) => + g.setNode(node.id, node as Node<{ label: string }, string | undefined> & { width?: number; height?: number }), + ); + + Dagre.layout(g); + + return { + nodes: nodes.map((node) => { + const { x, y } = g.node(node.id); + + return { ...node, position: { x, y } }; + }), + edges, + }; +}; + +const nodeTypes = { + serviceNode: KurtosisServiceNode, + artifactNode: KurtosisArtifactNode, + shellNode: KurtosisShellNode, + execNode: KurtosisExecNode, +}; + +export type VisualiserImperativeAttributes = { + getStarlark: () => string; +}; +type VisualiserProps = { + initialNodes: Node[]; + initialEdges: Edge[]; + existingEnclave?: RemoveFunctions; +}; +export const Visualiser = forwardRef( + ({ initialNodes, initialEdges, existingEnclave }, ref) => { + const { data, updateData } = useVariableContext(); + const insertOffset = useRef(0); + const { fitView, addNodes, getViewport } = useReactFlow(); + const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes || []); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges || []); + + const onLayout = useCallback(() => { + const layouted = getLayoutedElements(nodes, edges); + + setNodes([...layouted.nodes]); + setEdges([...layouted.edges]); + + window.requestAnimationFrame(() => { + fitView(); + }); + }, [nodes, edges, fitView, setEdges, setNodes]); + + const getNewNodePosition = (): XYPosition => { + const viewport = getViewport(); + insertOffset.current += 1; + return { x: -viewport.x + insertOffset.current * 20 + 400, y: -viewport.y + insertOffset.current * 20 }; + }; + + const handleAddServiceNode = () => { + const id = uuidv4(); + updateData(id, { + type: "service", + serviceName: "", + image: "", + ports: [], + env: [], + files: [], + isValid: false, + }); + addNodes({ + id, + position: getNewNodePosition(), + width: 650, + style: { width: "650px" }, + type: "serviceNode", + data: {}, + }); + }; + + const handleAddArtifactNode = () => { + const id = uuidv4(); + updateData(id, { type: "artifact", artifactName: "", files: {}, isValid: false }); + addNodes({ + id, + position: getNewNodePosition(), + width: 400, + style: { width: "400px" }, + type: "artifactNode", + data: {}, + }); + }; + + const handleAddShellNode = () => { + const id = uuidv4(); + updateData(id, { + type: "shell", + shellName: "", + command: "", + image: "", + env: [], + files: [], + store: "", + wait_enabled: "true", + wait: "", + isValid: false, + }); + addNodes({ + id, + position: getNewNodePosition(), + width: 650, + style: { width: "650px" }, + type: "shellNode", + data: {}, + }); + }; + + const handleAddExecNode = () => { + const id = uuidv4(); + updateData(id, { + type: "exec", + execName: "", + serviceName: "", + command: "", + acceptableCodes: [], + isValid: false, + }); + addNodes({ + id, + position: getNewNodePosition(), + width: 400, + style: { width: "400px" }, + type: "execNode", + data: {}, + }); + }; + + const handleNodeDoubleClick = useCallback( + (e: React.MouseEvent, node: Node) => { + fitView({ nodes: [node], maxZoom: 1, duration: 500 }); + }, + [fitView], + ); + + useEffect(() => { + setEdges((prevState) => { + return Object.entries(getNodeDependencies(data)).flatMap(([to, froms]) => + [...froms].map((from) => ({ + id: `${from}-${to}`, + source: from, + target: to, + animated: true, + style: { strokeWidth: "3px" }, + })), + ); + }); + }, [setEdges, data]); + + // Remove the resizeObserver error + useEffect(() => { + const errorHandler = (e: any) => { + if ( + e.message.includes( + "ResizeObserver loop completed with undelivered notifications" || "ResizeObserver loop limit exceeded", + ) + ) { + const resizeObserverErr = document.getElementById("webpack-dev-server-client-overlay"); + if (resizeObserverErr) { + resizeObserverErr.style.display = "none"; + } + } + }; + window.addEventListener("error", errorHandler); + + return () => { + window.removeEventListener("error", errorHandler); + }; + }, []); + + useImperativeHandle( + ref, + () => ({ + getStarlark: () => { + return generateStarlarkFromGraph(nodes, edges, data, existingEnclave); + }, + }), + [nodes, edges, data, existingEnclave], + ); + + return ( + + + + + + + + + + (insertOffset.current = 1)} + onNodesChange={onNodesChange} + onEdgesChange={onEdgesChange} + onNodeDoubleClick={handleNodeDoubleClick} + nodeTypes={nodeTypes} + fitView + > + + + + + + ); + }, +); +Visualiser.displayName = "ForwardRef Visualiser"; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx index 7684098336..83db6c565c 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx @@ -27,7 +27,9 @@ export const MentionStringArgumentInput = ({ } const suggestions = variables.map((v) => ({ display: v.displayName, id: v.id })); const queryTerms = query.toLowerCase().split(/\s+|\./); - return suggestions.filter((variable) => queryTerms.every((term) => variable.display.includes(term))); + return suggestions.filter((variable) => + queryTerms.every((term) => variable.display.toLowerCase().includes(term)), + ); }, [variables], ); @@ -56,7 +58,7 @@ export const MentionStringArgumentInput = ({ > diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MountArtifactFileInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MountArtifactFileInput.tsx new file mode 100644 index 0000000000..8f0631044b --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MountArtifactFileInput.tsx @@ -0,0 +1,38 @@ +import { Grid, GridItem } from "@chakra-ui/react"; +import { useMemo } from "react"; +import { SelectArgumentInput, SelectOption } from "../../../form/SelectArgumentInput"; +import { StringArgumentInput } from "../../../form/StringArgumentInput"; +import { KurtosisFormInputProps } from "../../../form/types"; +import { KurtosisServiceNodeData } from "../types"; +import { useVariableContext } from "../VariableContextProvider"; + +export const MountArtifactFileInput = (props: KurtosisFormInputProps) => { + const { variables } = useVariableContext(); + const artifactVariableOptions = useMemo((): SelectOption[] => { + return variables + .filter((variable) => variable.id.match(/^(?:artifact|shell)\.[^.]+$/)) + .map((variable) => ({ display: variable.displayName, value: `{{${variable.id}}}` })); + }, [variables]); + + return ( + + + + {...props} + size={"sm"} + placeholder={"/some/path"} + name={`${props.name as `files.${number}`}.mountPoint`} + /> + + + + options={artifactVariableOptions} + {...props} + size={"sm"} + placeholder={"Select an Artifact"} + name={`${props.name as `files.${number}`}.artifactName`} + /> + + + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/PortConfigurationInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/PortConfigurationInput.tsx new file mode 100644 index 0000000000..b9a8760ac3 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/PortConfigurationInput.tsx @@ -0,0 +1,49 @@ +import { Grid, GridItem } from "@chakra-ui/react"; +import { IntegerArgumentInput } from "../../../form/IntegerArgumentInput"; +import { OptionsArgumentInput } from "../../../form/OptionArgumentInput"; +import { StringArgumentInput } from "../../../form/StringArgumentInput"; +import { KurtosisFormInputProps } from "../../../form/types"; +import { KurtosisServiceNodeData } from "../types"; + +export const PortConfigurationField = (props: KurtosisFormInputProps) => ( + + + + {...props} + size={"sm"} + placeholder={"Port Name (eg postgres)"} + name={`${props.name as `ports.${number}`}.portName`} + /> + + + + {...props} + size={"sm"} + placeholder={"Application Protocol (eg postgresql)"} + name={`${props.name as `ports.${number}`}.applicationProtocol`} + validate={(val) => { + if (typeof val !== "string") { + return "Value should be a string"; + } + if (val.includes(" ")) { + return "Application protocol cannot include spaces"; + } + }} + /> + + + + {...props} + options={["TCP", "UDP"]} + name={`${props.name as `ports.${number}`}.transportProtocol`} + /> + + + + {...props} + name={`${props.name as `ports.${number}`}.port`} + size={"sm"} + /> + + +); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/validators.ts b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/validators.ts new file mode 100644 index 0000000000..528f1226d7 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/validators.ts @@ -0,0 +1,48 @@ +export function validateName(value?: string) { + if (typeof value !== "string") { + return "Value should be a string"; + } + if (value.match(/^\d+/)) { + return "Value cannot start with numbers"; + } +} +export function validateDockerLocator(value?: string) { + if (typeof value !== "string") { + return "Value should be a string"; + } + if (value === "") { + return; + } + + if ( + !value.match( + /^(?[\w.\-_]+((?::\d+|)(?=\/[a-z0-9._-]+\/[a-z0-9._-]+))|)(?:\/|)(?[a-z0-9.\-_]+(?:\/[a-z0-9.\-_]+|))(:(?[\w.\-_]{1,127})|)$/gim, + ) + ) { + return "Value does not look like a docker image"; + } +} + +export function validateDurationString(value?: string) { + if (typeof value !== "string") { + return "Value should be a string"; + } + if (value === "") { + return; + } + + if (!value.match(/^\d+[msd]?$/)) { + return "Value should be a custom wait duration with like '10s' or '3m'."; + } +} + +export function combineValidators(...validators: ((v?: string) => string | void)[]): (v?: string) => string | void { + return function (v?: string) { + for (const validator of validators) { + const r = validator(v); + if (r) { + return r; + } + } + }; +} diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts index e17cd84d48..b11df47b8e 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts @@ -3,3 +3,65 @@ export type Variable = { displayName: string; value: string; }; + +export type KurtosisPort = { + portName: string; + port: number; + transportProtocol: "TCP" | "UDP"; + applicationProtocol: string; +}; + +export type KurtosisEnvironmentVar = { key: string; value: string }; + +export type KurtosisFileMount = { + mountPoint: string; + artifactName: string; +}; + +export type KurtosisAcceptableCode = { + value: number; +}; + +export type KurtosisExecNodeData = { + type: "exec"; + execName: string; + serviceName: string; + command: string; + acceptableCodes: KurtosisAcceptableCode[]; + isValid: boolean; +}; + +export type KurtosisServiceNodeData = { + type: "service"; + serviceName: string; + image: string; + env: KurtosisEnvironmentVar[]; + ports: KurtosisPort[]; + files: KurtosisFileMount[]; + isValid: boolean; +}; +export type KurtosisArtifactNodeData = { + type: "artifact"; + artifactName: string; + files: Record; + isValid: boolean; +}; + +export type KurtosisShellNodeData = { + type: "shell"; + shellName: string; + command: string; + image: string; + env: KurtosisEnvironmentVar[]; + files: KurtosisFileMount[]; + store: string; + wait_enabled: "true" | "false"; + wait: string; + isValid: boolean; +}; + +export type KurtosisNodeData = + | KurtosisArtifactNodeData + | KurtosisServiceNodeData + | KurtosisShellNodeData + | KurtosisExecNodeData; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts index 4ee26fec42..87e53645a2 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts @@ -2,8 +2,7 @@ import { isDefined, RemoveFunctions, stringifyError } from "kurtosis-ui-componen import { Edge, Node } from "reactflow"; import { Result } from "true-myth"; import { EnclaveFullInfo } from "../../../types"; -import { Variable } from "./types"; -import { KurtosisNodeData, KurtosisServiceNodeData } from "./VariableContextProvider"; +import { KurtosisNodeData, KurtosisServiceNodeData, Variable } from "./types"; export const EMUI_BUILD_STATE_KEY = "EMUI_BUILD_STATE"; @@ -36,62 +35,102 @@ export function getInitialGraphStateFromEnclave( } } +export function getNodeName(kurtosisNodeData: KurtosisNodeData): string { + if (kurtosisNodeData.type === "service") { + return kurtosisNodeData.serviceName; + } + if (kurtosisNodeData.type === "artifact") { + return kurtosisNodeData.artifactName; + } + if (kurtosisNodeData.type === "shell") { + return kurtosisNodeData.shellName; + } + if (kurtosisNodeData.type === "exec") { + return kurtosisNodeData.execName; + } + throw new Error(`Unknown node type.`); +} + function normaliseNameToStarlarkVariable(name: string) { return name.replace(/\s|-/g, "_").toLowerCase(); } -const variablePattern = /\{\{((?:service|artifact).([^.]+)\.?.*)}}/; +function escapeString(value: string): string { + return value.replaceAll(/(["\\])/g, "\\$1"); +} + +const variablePattern = /\{\{((?:service|artifact|shell).([^.]+)\.?.*)}}/; export function getVariablesFromNodes(nodes: Record): Variable[] { - return Object.entries(nodes).flatMap(([id, data]) => - data.type === "service" - ? [ + return Object.entries(nodes).flatMap(([id, data]) => { + if (data.type === "service") { + return [ + { + id: `service.${id}.name`, + displayName: `service.${data.serviceName}.name`, + value: `${normaliseNameToStarlarkVariable(data.serviceName)}.name`, + }, + { + id: `service.${id}.hostname`, + displayName: `service.${data.serviceName}.hostname`, + value: `${normaliseNameToStarlarkVariable(data.serviceName)}.hostname`, + }, + ...data.ports.flatMap((port, i) => [ { - id: `service.${id}.name`, - displayName: `service.${data.serviceName}.name`, - value: `${normaliseNameToStarlarkVariable(data.serviceName)}.name`, + id: `service.${id}.port.${i}`, + displayName: `service.${data.serviceName}.port.${port.portName}`, + value: `"{}://{}:{}".format(${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${ + port.portName + }"].application_protocol, ${normaliseNameToStarlarkVariable( + data.serviceName, + )}.hostname, ${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${port.portName}"].number)`, }, { - id: `service.${id}.hostname`, - displayName: `service.${data.serviceName}.hostname`, - value: `${normaliseNameToStarlarkVariable(data.serviceName)}.hostname`, + id: `service.${id}.port.${i}.port`, + displayName: `service.${data.serviceName}.port.${port.portName}.port`, + value: `${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${port.portName}"].number`, }, - ...data.ports.flatMap((port, i) => [ - { - id: `service.${id}.port.${i}`, - displayName: `service.${data.serviceName}.port.${port.portName}`, - value: `"{}://{}:{}".format(${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${ - port.portName - }"].application_protocol, ${normaliseNameToStarlarkVariable( - data.serviceName, - )}.hostname, ${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${port.portName}"].number)`, - }, - { - id: `service.${id}.port.${i}.port`, - displayName: `service.${data.serviceName}.port.${port.portName}.port`, - value: `${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${port.portName}"].number`, - }, - { - id: `service.${id}.port.${i}.applicationProtocol`, - displayName: `service.${data.serviceName}.port.${port.portName}.application_protocol`, - value: `${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${ - port.portName - }"].application_protocol`, - }, - ]), - ...data.env.map((env, i) => ({ - id: `service.${id}.env.${i}`, - displayName: `service.${data.serviceName}.env.${env.key}`, - value: `"${env.value}"`, - })), - ] - : [ { - id: `artifact.${id}`, - displayName: `artifact.${data.artifactName}`, - value: `${normaliseNameToStarlarkVariable(data.artifactName)}`, + id: `service.${id}.port.${i}.applicationProtocol`, + displayName: `service.${data.serviceName}.port.${port.portName}.application_protocol`, + value: `${normaliseNameToStarlarkVariable(data.serviceName)}.ports["${ + port.portName + }"].application_protocol`, }, - ], - ); + ]), + ...data.env.map((env, i) => ({ + id: `service.${id}.env.${i}`, + displayName: `service.${data.serviceName}.env.${env.key}`, + value: `"${env.value}"`, + })), + ]; + } + if (data.type === "artifact") { + return [ + { + id: `artifact.${id}`, + displayName: `artifact.${data.artifactName}`, + value: `${normaliseNameToStarlarkVariable(data.artifactName)}`, + }, + ]; + } + + if (data.type === "shell") { + return [ + { + id: `shell.${id}`, + displayName: `shell.${data.shellName}`, + value: `${normaliseNameToStarlarkVariable(data.shellName)}.files_artifacts[0]`, + }, + ...data.env.map((env, i) => ({ + id: `shell.${id}.env.${i}`, + displayName: `shell.${data.shellName}.env.${env.key}`, + value: `"${env.value}"`, + })), + ]; + } + + return []; + }); } export function getNodeDependencies(nodes: Record): Record> { @@ -127,6 +166,38 @@ export function getNodeDependencies(nodes: Record): Re } }); } + if (data.type === "shell") { + const nameMatches = data.shellName.match(variablePattern); + if (nameMatches) { + getDependenciesFor(id).add(nameMatches[2]); + } + data.env.forEach((env) => { + const envMatches = env.key.match(variablePattern) || env.value.match(variablePattern); + if (envMatches) { + getDependenciesFor(id).add(envMatches[2]); + } + }); + data.files.forEach((file) => { + const fileMatches = file.mountPoint.match(variablePattern) || file.artifactName.match(variablePattern); + if (fileMatches) { + getDependenciesFor(id).add(fileMatches[2]); + } + }); + } + if (data.type === "exec") { + const nameMatches = data.execName.match(variablePattern); + if (nameMatches) { + getDependenciesFor(id).add(nameMatches[2]); + } + const serviceMatches = data.serviceName.match(variablePattern); + if (serviceMatches) { + getDependenciesFor(id).add(serviceMatches[2]); + } + const commandMatches = data.command.match(variablePattern); + if (commandMatches) { + getDependenciesFor(id).add(commandMatches[2]); + } + } }); return dependencies; } @@ -215,13 +286,54 @@ export function generateStarlarkFromGraph( starlark += ` config = {\n`; for (const [fileName, fileText] of Object.entries(nodeData.files)) { starlark += ` "${fileName}": struct(\n`; - starlark += ` template="""${fileText}""",\n`; + starlark += ` template="""${escapeString(fileText)}""",\n`; starlark += ` data={},\n`; starlark += ` ),\n`; } starlark += ` },\n`; starlark += ` )\n\n`; } + + if (nodeData.type === "shell") { + const shellName = normaliseNameToStarlarkVariable(nodeData.shellName); + starlark += ` ${shellName} = plan.run_sh(\n`; + starlark += ` run = """${escapeString(nodeData.command)}""",\n`; + const image = interpolateValue(nodeData.image); + if (image !== '""') { + starlark += ` image = ${image},\n`; + } + starlark += ` env_vars = {\n`; + for (const { key, value } of nodeData.env) { + starlark += ` ${interpolateValue(key)}: ${interpolateValue(value)},\n`; + } + starlark += ` },\n`; + starlark += ` files = {\n`; + for (const { mountPoint, artifactName } of nodeData.files) { + starlark += ` ${interpolateValue(mountPoint)}: ${interpolateValue(artifactName)},\n`; + } + starlark += ` },\n`; + starlark += ` store = [\n`; + starlark += ` StoreSpec(src = ${interpolateValue(nodeData.store)}, name="${shellName}"),\n`; + starlark += ` ],\n`; + const wait = interpolateValue(nodeData.wait); + if (nodeData.wait_enabled === "false" || wait !== '""') { + starlark += ` wait=${nodeData.wait_enabled === "true" ? wait : "None"},\n`; + } + starlark += ` )\n\n`; + } + + if (nodeData.type === "exec") { + const execName = normaliseNameToStarlarkVariable(nodeData.execName); + starlark += ` ${execName} = plan.exec(\n`; + starlark += ` service_name = ${interpolateValue(nodeData.serviceName)},\n`; + starlark += ` recipe = ExecRecipe(\n`; + starlark += ` command = [${nodeData.command.split(" ").map(interpolateValue).join(", ")}],`; + starlark += ` ),\n`; + if (nodeData.acceptableCodes.length > 0) { + starlark += ` acceptable_codes = [${nodeData.acceptableCodes.map(({ value }) => value).join(", ")}],\n`; + } + starlark += ` )\n\n`; + } } // Delete any services from any existing enclave that aren't defined anymore From e9a977951bb892f05b1b62e607d5ea98e13cd4d7 Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Thu, 15 Feb 2024 02:31:13 -0500 Subject: [PATCH 057/102] chore(main): release 0.86.20 (#2159) :robot: I have created a release *beep* *boop* --- ## [0.86.20](https://github.com/kurtosis-tech/kurtosis/compare/0.86.19...0.86.20) (2024-02-14) ### Features * support `run_sh` and `exec` in enclave builder ([#2158](https://github.com/kurtosis-tech/kurtosis/issues/2158)) ([f784eaf](https://github.com/kurtosis-tech/kurtosis/commit/f784eaf7a24ae282aa470d22e6a9ad721d04cc05)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 7 +++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- api/typescript/src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 18 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14c8427bfe..341bd71d44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.86.20](https://github.com/kurtosis-tech/kurtosis/compare/0.86.19...0.86.20) (2024-02-14) + + +### Features + +* support `run_sh` and `exec` in enclave builder ([#2158](https://github.com/kurtosis-tech/kurtosis/issues/2158)) ([f784eaf](https://github.com/kurtosis-tech/kurtosis/commit/f784eaf7a24ae282aa470d22e6a9ad721d04cc05)) + ## [0.86.19](https://github.com/kurtosis-tech/kurtosis/compare/0.86.18...0.86.19) (2024-02-09) diff --git a/LICENSE.md b/LICENSE.md index 40946a206a..ffe15804e2 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.19 +Licensed Work: Kurtosis 0.86.20 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-02-09 +Change Date: 2028-02-14 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 7ff326bd39..1c7e4588c9 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.19" + KurtosisVersion = "0.86.20" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index ee1dab1ca3..83a5005ca2 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.19" +version = "0.86.20" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index 17bba4117c..08da35f9f9 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.19", + "version": "0.86.20", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index e467c095ff..ebf74ae916 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.19" +export const KURTOSIS_VERSION: string = "0.86.20" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index ab4b306ef9..27c813ad88 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.19", + "version": "0.86.20", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index 9a579db5fe..8d7a248b21 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.19", + "version": "0.86.20", "private": true, "homepage": ".", "dependencies": { @@ -10,7 +10,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.19", + "kurtosis-ui-components": "0.86.20", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "react-mentions": "^4.4.10", diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index 5489cb0d28..941994af62 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.19", + "version": "0.86.20", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 4c134a2757..63c66cbfde 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.19 +0.86.20 From 807ddae12b9274819f53160a8771da7541f4a4c1 Mon Sep 17 00:00:00 2001 From: Edgar Gomes Date: Thu, 15 Feb 2024 06:46:10 -0300 Subject: [PATCH 058/102] fix: Core image builds for arm64 under CI (#2149) ## Description: Attempt to fix [this error](https://app.circleci.com/pipelines/github/kurtosis-tech/kurtosis/11230/workflows/88eae5e6-f3fd-4895-9af0-2685895b59d3/jobs/162867) possible linked to https://github.com/NixOS/nix/issues/5258. ## Is this change user facing? NO --- core/server/Dockerfile | 4 ++++ core/server/Dockerfile.debug | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/core/server/Dockerfile b/core/server/Dockerfile index cd57de82c6..83fa2599d1 100644 --- a/core/server/Dockerfile +++ b/core/server/Dockerfile @@ -2,6 +2,10 @@ FROM alpine:3.17 # We need protobut-dev to run protobuf compiler against startosis .proto files RUN apk update && apk add --no-cache bash protobuf-dev sudo shadow curl xz + +# Install Nix +# We need to set filter-syscalls to false to allow Nix to work properly inside a container: https://github.com/NixOS/nix/issues/5258 +ENV NIX_CONFIG=$'filter-syscalls = false\nexperimental-features = nix-command flakes' RUN sh <(curl -L https://nixos.org/nix/install) --daemon --yes ARG TARGETARCH diff --git a/core/server/Dockerfile.debug b/core/server/Dockerfile.debug index 1aceb832e5..0061f97af8 100644 --- a/core/server/Dockerfile.debug +++ b/core/server/Dockerfile.debug @@ -2,6 +2,10 @@ FROM alpine:3.19 # We need protobut-dev to run protobuf compiler against startosis .proto files RUN apk update && apk add --no-cache bash protobuf-dev sudo shadow curl xz + +# Install Nix +# We need to set filter-syscalls to false to allow Nix to work properly inside a container: https://github.com/NixOS/nix/issues/5258 +ENV NIX_CONFIG=$'filter-syscalls = false\nexperimental-features = nix-command flakes' RUN sh <(curl -L https://nixos.org/nix/install) --daemon --yes # Make sure that you changed the port inside the APIC's code before changing it here From 32c049b4cb6a7b0b09ad0caa71482eef3f63ca46 Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Thu, 15 Feb 2024 06:06:25 -0500 Subject: [PATCH 059/102] chore(main): release 0.86.21 (#2160) :robot: I have created a release *beep* *boop* --- ## [0.86.21](https://github.com/kurtosis-tech/kurtosis/compare/0.86.20...0.86.21) (2024-02-15) ### Bug Fixes * Core image builds for arm64 under CI ([#2149](https://github.com/kurtosis-tech/kurtosis/issues/2149)) ([807ddae](https://github.com/kurtosis-tech/kurtosis/commit/807ddae12b9274819f53160a8771da7541f4a4c1)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 7 +++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- api/typescript/src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 18 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 341bd71d44..8555e6bcc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.86.21](https://github.com/kurtosis-tech/kurtosis/compare/0.86.20...0.86.21) (2024-02-15) + + +### Bug Fixes + +* Core image builds for arm64 under CI ([#2149](https://github.com/kurtosis-tech/kurtosis/issues/2149)) ([807ddae](https://github.com/kurtosis-tech/kurtosis/commit/807ddae12b9274819f53160a8771da7541f4a4c1)) + ## [0.86.20](https://github.com/kurtosis-tech/kurtosis/compare/0.86.19...0.86.20) (2024-02-14) diff --git a/LICENSE.md b/LICENSE.md index ffe15804e2..22c5df34cb 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.20 +Licensed Work: Kurtosis 0.86.21 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-02-14 +Change Date: 2028-02-15 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 1c7e4588c9..39846c1700 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.20" + KurtosisVersion = "0.86.21" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 83a5005ca2..2bb54ac4f9 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.20" +version = "0.86.21" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index 08da35f9f9..48524be958 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.20", + "version": "0.86.21", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index ebf74ae916..25ea10e87b 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.20" +export const KURTOSIS_VERSION: string = "0.86.21" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 27c813ad88..3aa2007da1 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.20", + "version": "0.86.21", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index 8d7a248b21..24b98767b7 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.20", + "version": "0.86.21", "private": true, "homepage": ".", "dependencies": { @@ -10,7 +10,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.20", + "kurtosis-ui-components": "0.86.21", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "react-mentions": "^4.4.10", diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index 941994af62..df30883870 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.20", + "version": "0.86.21", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 63c66cbfde..785922b0b8 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.20 +0.86.21 From 38c225ed89d3568c38361ffe57d7370c4c5e0c06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 16:54:38 +0530 Subject: [PATCH 060/102] build(deps): Bump google.golang.org/grpc from 1.56.2 to 1.56.3 in /internal_testsuites/golang (#1624) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.56.2 to 1.56.3.
Release notes

Sourced from google.golang.org/grpc's releases.

Release 1.56.3

Security

  • server: prohibit more than MaxConcurrentStreams handlers from running at once (CVE-2023-44487)

    In addition to this change, applications should ensure they do not leave running tasks behind related to the RPC before returning from method handlers, or should enforce appropriate limits on any such work.

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/grpc&package-manager=go_modules&previous-version=1.56.2&new-version=1.56.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/kurtosis-tech/kurtosis/network/alerts).
> **Note** > Automatic rebases have been disabled on this pull request as it has been open for over 30 days. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gyanendra Mishra From 6cf770bd81366b0b20c19ab1ddb362783dc6b544 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:31:10 +0000 Subject: [PATCH 061/102] build(deps): Bump follow-redirects from 1.15.0 to 1.15.4 in /docs (#2035) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.0 to 1.15.4.
Commits
  • 6585820 Release version 1.15.4 of the npm package.
  • 7a6567e Disallow bracketed hostnames.
  • 05629af Prefer native URL instead of deprecated url.parse.
  • 1cba8e8 Prefer native URL instead of legacy url.resolve.
  • 72bc2a4 Simplify _processResponse error handling.
  • 3d42aec Add bracket tests.
  • bcbb096 Do not directly set Error properties.
  • 192dbe7 Release version 1.15.3 of the npm package.
  • bd8c81e Fix resource leak on destroy.
  • 9c728c3 Split linting and testing.
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=follow-redirects&package-manager=npm_and_yarn&previous-version=1.15.0&new-version=1.15.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/kurtosis-tech/kurtosis/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gyanendra Mishra --- docs/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/yarn.lock b/docs/yarn.lock index 3d3f411a12..4b50e3a37e 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -4147,9 +4147,9 @@ flux@^4.0.1: fbjs "^3.0.1" follow-redirects@^1.0.0, follow-redirects@^1.14.7: - version "1.15.0" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz" - integrity sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ== + version "1.15.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" + integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== fork-ts-checker-webpack-plugin@^6.5.0: version "6.5.2" From acd884fb1a8292b450e66c8f48156f4cef52a082 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Thu, 15 Feb 2024 17:43:01 +0530 Subject: [PATCH 062/102] fix: allow building images in arm64 (#2161) This should allow building images in arm64, fixing our release issue. I have tried this by running the docker image builder script locally ( on mac) to produce an arm64 image --- core/server/Dockerfile | 2 +- core/server/Dockerfile.debug | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/server/Dockerfile b/core/server/Dockerfile index 83fa2599d1..31a9b1f382 100644 --- a/core/server/Dockerfile +++ b/core/server/Dockerfile @@ -5,7 +5,7 @@ RUN apk update && apk add --no-cache bash protobuf-dev sudo shadow curl xz # Install Nix # We need to set filter-syscalls to false to allow Nix to work properly inside a container: https://github.com/NixOS/nix/issues/5258 -ENV NIX_CONFIG=$'filter-syscalls = false\nexperimental-features = nix-command flakes' +ENV NIX_INSTALL_ARGS="--extra-conf 'filter-syscalls = false'" RUN sh <(curl -L https://nixos.org/nix/install) --daemon --yes ARG TARGETARCH diff --git a/core/server/Dockerfile.debug b/core/server/Dockerfile.debug index 0061f97af8..212f5d92f4 100644 --- a/core/server/Dockerfile.debug +++ b/core/server/Dockerfile.debug @@ -5,7 +5,7 @@ RUN apk update && apk add --no-cache bash protobuf-dev sudo shadow curl xz # Install Nix # We need to set filter-syscalls to false to allow Nix to work properly inside a container: https://github.com/NixOS/nix/issues/5258 -ENV NIX_CONFIG=$'filter-syscalls = false\nexperimental-features = nix-command flakes' +ENV NIX_INSTALL_ARGS="--extra-conf 'filter-syscalls = false'" RUN sh <(curl -L https://nixos.org/nix/install) --daemon --yes # Make sure that you changed the port inside the APIC's code before changing it here From ee4d4cabdf22f2b473373411090d6b012e5375d8 Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Thu, 15 Feb 2024 07:39:14 -0500 Subject: [PATCH 063/102] chore(main): release 0.86.22 (#2162) :robot: I have created a release *beep* *boop* --- ## [0.86.22](https://github.com/kurtosis-tech/kurtosis/compare/0.86.21...0.86.22) (2024-02-15) ### Bug Fixes * allow building images in arm64 ([#2161](https://github.com/kurtosis-tech/kurtosis/issues/2161)) ([acd884f](https://github.com/kurtosis-tech/kurtosis/commit/acd884fb1a8292b450e66c8f48156f4cef52a082)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 7 +++++++ LICENSE.md | 2 +- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- api/typescript/src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8555e6bcc2..07ed901418 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.86.22](https://github.com/kurtosis-tech/kurtosis/compare/0.86.21...0.86.22) (2024-02-15) + + +### Bug Fixes + +* allow building images in arm64 ([#2161](https://github.com/kurtosis-tech/kurtosis/issues/2161)) ([acd884f](https://github.com/kurtosis-tech/kurtosis/commit/acd884fb1a8292b450e66c8f48156f4cef52a082)) + ## [0.86.21](https://github.com/kurtosis-tech/kurtosis/compare/0.86.20...0.86.21) (2024-02-15) diff --git a/LICENSE.md b/LICENSE.md index 22c5df34cb..871c36fbb8 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.21 +Licensed Work: Kurtosis 0.86.22 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 39846c1700..b9a79d1b75 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.21" + KurtosisVersion = "0.86.22" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 2bb54ac4f9..cd7eae102b 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.21" +version = "0.86.22" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index 48524be958..45a5b3a29b 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.21", + "version": "0.86.22", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index 25ea10e87b..4034304cf5 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.21" +export const KURTOSIS_VERSION: string = "0.86.22" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 3aa2007da1..284ae64f52 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.21", + "version": "0.86.22", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index 24b98767b7..197166c175 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.21", + "version": "0.86.22", "private": true, "homepage": ".", "dependencies": { @@ -10,7 +10,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.21", + "kurtosis-ui-components": "0.86.22", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "react-mentions": "^4.4.10", diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index df30883870..d46ba89132 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.21", + "version": "0.86.22", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 785922b0b8..bb774beb79 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.21 +0.86.22 From 8f68547ed7ec0959dabd5c2fa2ddec1a78f43847 Mon Sep 17 00:00:00 2001 From: Edgar Gomes Date: Thu, 15 Feb 2024 10:45:21 -0300 Subject: [PATCH 064/102] fix: replace nix installer (#2163) ## Description: Fix the cross platform docker images that are trying to install nix by replacing the installer and passing extra config params. ## Is this change user facing? NO ## References (if applicable): - https://github.com/DeterminateSystems/nix-installer/issues/324 - https://github.com/NixOS/nix/issues/5258 --- core/server/Dockerfile | 7 ++++--- core/server/Dockerfile.debug | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/core/server/Dockerfile b/core/server/Dockerfile index 31a9b1f382..f601298e67 100644 --- a/core/server/Dockerfile +++ b/core/server/Dockerfile @@ -4,9 +4,10 @@ FROM alpine:3.17 RUN apk update && apk add --no-cache bash protobuf-dev sudo shadow curl xz # Install Nix -# We need to set filter-syscalls to false to allow Nix to work properly inside a container: https://github.com/NixOS/nix/issues/5258 -ENV NIX_INSTALL_ARGS="--extra-conf 'filter-syscalls = false'" -RUN sh <(curl -L https://nixos.org/nix/install) --daemon --yes +# We need to set filter-syscalls to false to allow Nix install to work properly inside a container with cross platform emulation +# via QEMU: https://github.com/NixOS/nix/issues/5258 and use a more flexible installer https://github.com/DeterminateSystems/nix-installer +# with a workaround on the same issue: https://github.com/DeterminateSystems/nix-installer/issues/324) +RUN curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install linux --no-confirm --init none --extra-conf "filter-syscalls = false" ARG TARGETARCH diff --git a/core/server/Dockerfile.debug b/core/server/Dockerfile.debug index 212f5d92f4..a224ddcf48 100644 --- a/core/server/Dockerfile.debug +++ b/core/server/Dockerfile.debug @@ -4,9 +4,10 @@ FROM alpine:3.19 RUN apk update && apk add --no-cache bash protobuf-dev sudo shadow curl xz # Install Nix -# We need to set filter-syscalls to false to allow Nix to work properly inside a container: https://github.com/NixOS/nix/issues/5258 -ENV NIX_INSTALL_ARGS="--extra-conf 'filter-syscalls = false'" -RUN sh <(curl -L https://nixos.org/nix/install) --daemon --yes +# We need to set filter-syscalls to false to allow Nix install to work properly inside a container with cross platform emulation +# via QEMU: https://github.com/NixOS/nix/issues/5258 and use a more flexible installer https://github.com/DeterminateSystems/nix-installer +# with a workaround on the same issue: https://github.com/DeterminateSystems/nix-installer/issues/324) +RUN curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install linux --no-confirm --init none --extra-conf "filter-syscalls = false" # Make sure that you changed the port inside the APIC's code before changing it here EXPOSE 50103 From a84c631eb3dd1b713d117bbaa86519a848e1598f Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Thu, 15 Feb 2024 09:10:38 -0500 Subject: [PATCH 065/102] chore(main): release 0.86.23 (#2164) :robot: I have created a release *beep* *boop* --- ## [0.86.23](https://github.com/kurtosis-tech/kurtosis/compare/0.86.22...0.86.23) (2024-02-15) ### Bug Fixes * replace nix installer ([#2163](https://github.com/kurtosis-tech/kurtosis/issues/2163)) ([8f68547](https://github.com/kurtosis-tech/kurtosis/commit/8f68547ed7ec0959dabd5c2fa2ddec1a78f43847)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 7 +++++++ LICENSE.md | 2 +- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- api/typescript/src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07ed901418..bdb466d21d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.86.23](https://github.com/kurtosis-tech/kurtosis/compare/0.86.22...0.86.23) (2024-02-15) + + +### Bug Fixes + +* replace nix installer ([#2163](https://github.com/kurtosis-tech/kurtosis/issues/2163)) ([8f68547](https://github.com/kurtosis-tech/kurtosis/commit/8f68547ed7ec0959dabd5c2fa2ddec1a78f43847)) + ## [0.86.22](https://github.com/kurtosis-tech/kurtosis/compare/0.86.21...0.86.22) (2024-02-15) diff --git a/LICENSE.md b/LICENSE.md index 871c36fbb8..da1d5d699c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.22 +Licensed Work: Kurtosis 0.86.23 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index b9a79d1b75..007fc83885 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.22" + KurtosisVersion = "0.86.23" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index cd7eae102b..7a551ed10a 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.22" +version = "0.86.23" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index 45a5b3a29b..8b64e6d9e1 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.22", + "version": "0.86.23", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index 4034304cf5..18f1e20174 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.22" +export const KURTOSIS_VERSION: string = "0.86.23" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 284ae64f52..8e5907d739 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.22", + "version": "0.86.23", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index 197166c175..c0ff51cb1d 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.22", + "version": "0.86.23", "private": true, "homepage": ".", "dependencies": { @@ -10,7 +10,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.22", + "kurtosis-ui-components": "0.86.23", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "react-mentions": "^4.4.10", diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index d46ba89132..be232d3760 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.22", + "version": "0.86.23", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index bb774beb79..23b9e21456 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.22 +0.86.23 From 9085cfd7d1ad51b65e2087e124cc24cb487364b8 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Thu, 15 Feb 2024 19:50:18 +0530 Subject: [PATCH 066/102] feat: added a `description` field to instructions (#2147) ## Description The description field can be used instead of the full instruction while outputting to the end user In the CLI this can be accessed using `--description`; --- .../api_container_service.pb.go | 864 +++++++------- .../binding_constructors.go | 3 +- api/protobuf/core/api_container_service.proto | 2 + api/rust/src/api_container_api.rs | 2 + .../api_container_service_pb.d.ts | 4 + .../api_container_service_pb.js | 32 +- .../connect/api_container_service_pb.d.ts | 5 + .../connect/api_container_service_pb.js | 1 + cli/cli/command_args/run/verbosity.go | 1 + cli/cli/command_args/run/verbosity_enumer.go | 12 +- cli/cli/commands/run/run.go | 2 +- .../kurtosis_instruction_printer.go | 2 + .../kurtosis_instruction_printer_test.go | 6 +- .../add_service/add_service.go | 4 + .../add_service/add_services.go | 13 + .../kurtosis_instruction/exec/exec.go | 4 + .../kurtosis_print/kurtosis_print.go | 4 + .../remove_service/remove_service.go | 4 + .../render_templates/render_templates.go | 4 + .../kurtosis_instruction/request/request.go | 5 + .../start_service/start_service.go | 4 + .../stop_service/stop_service.go | 4 + .../store_service_files.go | 4 + .../kurtosis_instruction/tasks/run_python.go | 4 + .../kurtosis_instruction/tasks/run_sh.go | 4 + .../upload_files/upload_files.go | 4 + .../kurtosis_instruction/verify/verify.go | 4 + .../kurtosis_instruction/wait/wait.go | 4 + .../kurtosis_plan_instruction_capabilities.go | 3 + .../kurtosis_plan_instruction_internal.go | 2 +- .../startosis_executor_test.go | 34 +- package-lock.json | 703 +++++++++++- package.json | 3 + yarn.lock | 1002 +++++++++++++---- 34 files changed, 2091 insertions(+), 662 deletions(-) diff --git a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go index a8d4f05aea..6c9a6ffbdf 100644 --- a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go +++ b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go @@ -1200,6 +1200,7 @@ type StarlarkInstruction struct { Arguments []*StarlarkInstructionArg `protobuf:"bytes,3,rep,name=arguments,proto3" json:"arguments,omitempty"` ExecutableInstruction string `protobuf:"bytes,4,opt,name=executable_instruction,json=executableInstruction,proto3" json:"executable_instruction,omitempty"` IsSkipped bool `protobuf:"varint,5,opt,name=is_skipped,json=isSkipped,proto3" json:"is_skipped,omitempty"` + Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"` } func (x *StarlarkInstruction) Reset() { @@ -1269,6 +1270,13 @@ func (x *StarlarkInstruction) GetIsSkipped() bool { return false } +func (x *StarlarkInstruction) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + type StarlarkInstructionResult struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3492,7 +3500,7 @@ var file_api_container_service_proto_rawDesc = []byte{ 0x72, 0x6b, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x22, 0xab, 0x02, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, + 0x67, 0x65, 0x22, 0xcd, 0x02, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, @@ -3511,442 +3519,444 @@ var file_api_container_service_proto_rawDesc = []byte{ 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, - 0x22, 0x5f, 0x0a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x42, 0x0a, - 0x1d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, - 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x22, 0xa4, 0x01, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, - 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x12, 0x30, 0x0a, 0x14, - 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x67, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x41, 0x72, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1e, - 0x0a, 0x08, 0x61, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x07, 0x61, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2b, - 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x52, 0x65, 0x70, - 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, - 0x61, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x1b, 0x53, 0x74, 0x61, 0x72, - 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x22, - 0xac, 0x02, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x12, 0x63, 0x0a, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, - 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, - 0x00, 0x52, 0x13, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x57, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0f, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x54, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, - 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x42, - 0x0a, 0x1b, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, - 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, - 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x22, 0x3e, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, - 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x22, 0x3d, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x22, 0x92, 0x01, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, - 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x65, - 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, - 0x74, 0x65, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x53, 0x74, 0x65, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x65, 0x70, - 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x6c, - 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, - 0x69, 0x73, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x12, - 0x30, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x73, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x88, 0x01, - 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, - 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xc5, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x6b, 0x0a, 0x13, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0xd1, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, - 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, - 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, - 0x6e, 0x66, 0x6f, 0x1a, 0x5e, 0x0a, 0x10, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0x5f, 0x0a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, + 0x42, 0x0a, 0x1d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x69, 0x6e, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x22, 0xa4, 0x01, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, + 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x12, 0x30, + 0x0a, 0x14, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x67, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x41, 0x72, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x1e, 0x0a, 0x08, 0x61, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x61, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x52, + 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x0b, 0x0a, + 0x09, 0x5f, 0x61, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x1b, 0x53, 0x74, + 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6c, + 0x75, 0x6d, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, + 0x6e, 0x22, 0xac, 0x02, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x12, 0x63, 0x0a, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x48, 0x00, 0x52, 0x13, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x57, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, + 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x12, 0x54, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, + 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x22, 0x42, 0x0a, 0x1b, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, + 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0x3e, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, + 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0x3d, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, + 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, + 0x52, 0x75, 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, + 0x74, 0x65, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x5f, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x53, 0x74, 0x65, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, + 0x65, 0x70, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x53, 0x74, 0x61, + 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x72, 0x75, 0x6e, 0x5f, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0f, 0x69, 0x73, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, + 0x6c, 0x12, 0x30, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, + 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xc5, 0x01, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x6b, 0x0a, + 0x13, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x70, 0x69, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0xd1, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0c, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x37, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x5e, 0x0a, 0x10, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x72, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x5f, + 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x65, 0x6e, 0x65, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x32, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4d, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x72, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x5f, 0x75, 0x75, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, - 0x6e, 0x65, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x32, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, - 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0e, 0x61, - 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x22, 0x63, 0x0a, - 0x0f, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, - 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, - 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, - 0x67, 0x73, 0x22, 0x51, 0x0a, 0x13, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x69, - 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, - 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x6f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x4f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xac, 0x03, 0x0a, 0x26, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, - 0x72, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, 0x67, 0x73, - 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, - 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, - 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, - 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, - 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x48, 0x01, 0x52, 0x18, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, - 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, - 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, - 0x48, 0x02, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x41, - 0x0a, 0x1a, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, - 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0d, 0x48, 0x03, 0x52, 0x18, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x44, 0x65, 0x6c, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, + 0x0e, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x22, + 0x63, 0x0a, 0x0f, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, + 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x72, 0x67, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x41, 0x72, 0x67, 0x73, 0x22, 0x51, 0x0a, 0x13, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x65, + 0x78, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, + 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xac, 0x03, 0x0a, 0x26, 0x57, 0x61, 0x69, 0x74, + 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, + 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x41, + 0x0a, 0x1a, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, + 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0d, 0x48, 0x01, 0x52, 0x18, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, - 0x01, 0x12, 0x20, 0x0a, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x08, 0x62, 0x6f, 0x64, 0x79, 0x54, 0x65, 0x78, 0x74, - 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x1d, 0x0a, 0x1b, - 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, - 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, - 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x65, 0x74, 0x72, - 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, - 0x74, 0x65, 0x78, 0x74, 0x22, 0xe6, 0x03, 0x0a, 0x27, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, - 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, 0x67, 0x73, - 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, - 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, - 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, - 0x79, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, - 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x18, 0x69, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, - 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, - 0x69, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x04, 0x52, 0x18, 0x72, 0x65, - 0x74, 0x72, 0x69, 0x65, 0x73, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x62, 0x6f, 0x64, - 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x08, - 0x62, 0x6f, 0x64, 0x79, 0x54, 0x65, 0x78, 0x74, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x48, 0x02, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x88, 0x01, 0x01, + 0x12, 0x41, 0x0a, 0x1a, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x18, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x44, + 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, + 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x08, 0x62, 0x6f, 0x64, 0x79, 0x54, 0x65, + 0x78, 0x74, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x1d, + 0x0a, 0x1b, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, + 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x65, + 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, + 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x62, 0x6f, 0x64, + 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x22, 0xe6, 0x03, 0x0a, 0x27, 0x57, 0x61, 0x69, 0x74, 0x46, + 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, + 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x26, + 0x0a, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, + 0x6f, 0x64, 0x79, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, - 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, - 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, - 0x0c, 0x0a, 0x0a, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x22, 0x99, 0x01, - 0x0a, 0x11, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, - 0x75, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x76, 0x69, - 0x6f, 0x75, 0x73, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x43, 0x68, - 0x75, 0x6e, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x40, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, - 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x27, 0x0a, 0x11, 0x44, 0x61, 0x74, - 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0x45, 0x0a, 0x1b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, - 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x19, 0x44, 0x6f, 0x77, - 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, - 0x63, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x19, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, - 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x41, - 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x33, 0x0a, 0x1d, 0x53, 0x74, 0x6f, - 0x72, 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, - 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x87, - 0x01, 0x0a, 0x21, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, - 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x25, 0x53, 0x74, 0x6f, 0x72, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, - 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x52, 0x0a, 0x18, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x18, 0x69, 0x6e, + 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x07, 0x72, 0x65, + 0x74, 0x72, 0x69, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, 0x72, 0x65, 0x74, 0x72, + 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x04, 0x52, 0x18, + 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, + 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x62, + 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, + 0x52, 0x08, 0x62, 0x6f, 0x64, 0x79, 0x54, 0x65, 0x78, 0x74, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, + 0x05, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, + 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x22, + 0x99, 0x01, 0x0a, 0x11, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x40, 0x0a, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, + 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x27, 0x0a, 0x11, 0x44, + 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x45, 0x0a, 0x1b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x19, 0x44, + 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x19, 0x53, 0x74, 0x6f, 0x72, + 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x33, 0x0a, 0x1d, 0x53, + 0x74, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, + 0x22, 0x87, 0x01, 0x0a, 0x21, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, + 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x25, 0x53, 0x74, + 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, + 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x52, 0x0a, 0x18, 0x46, 0x69, 0x6c, 0x65, 0x73, + 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, + 0x75, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x26, + 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x14, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x26, 0x4c, 0x69, - 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x14, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, - 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x52, - 0x11, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, - 0x64, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x23, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x13, 0x66, 0x69, - 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, 0x75, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x6c, 0x65, - 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, - 0x55, 0x75, 0x69, 0x64, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, - 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x8b, 0x01, 0x0a, 0x24, 0x49, 0x6e, 0x73, 0x70, 0x65, - 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x63, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x70, 0x69, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, - 0x69, 0x6c, 0x65, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x23, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x72, 0x74, - 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x69, 0x6c, - 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, - 0x73, 0x69, 0x7a, 0x65, 0x12, 0x26, 0x0a, 0x0c, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x72, 0x65, - 0x76, 0x69, 0x65, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, - 0x78, 0x74, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, - 0x5f, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x22, 0x4b, 0x0a, - 0x13, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x41, 0x72, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x03, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, - 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, - 0x2b, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x2b, 0x0a, 0x11, - 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, - 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, - 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x12, 0x3a, 0x0a, 0x1a, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x5f, - 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x16, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x54, 0x6f, 0x4d, - 0x61, 0x69, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x69, 0x6e, 0x5f, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x61, 0x69, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x5b, 0x0a, 0x15, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, - 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, - 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x14, 0x65, 0x78, - 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x52, - 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, 0x72, 0x65, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2a, 0x36, 0x0a, 0x0d, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, - 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x55, 0x4e, - 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, - 0x4e, 0x10, 0x02, 0x2a, 0x2c, 0x0a, 0x11, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x77, 0x6e, - 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x10, - 0x01, 0x2a, 0x26, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x0b, 0x0a, 0x07, - 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x4f, 0x5f, - 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x01, 0x2a, 0x32, 0x0a, 0x13, 0x4b, 0x75, 0x72, - 0x74, 0x6f, 0x73, 0x69, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, - 0x12, 0x1b, 0x0a, 0x17, 0x4e, 0x4f, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x49, - 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x2a, 0x26, 0x0a, - 0x0d, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x09, - 0x0a, 0x05, 0x4e, 0x45, 0x56, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4c, 0x57, - 0x41, 0x59, 0x53, 0x10, 0x01, 0x32, 0xce, 0x0e, 0x0a, 0x13, 0x41, 0x70, 0x69, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6d, 0x0a, - 0x11, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, - 0x72, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x2a, 0x2e, 0x61, + 0x64, 0x52, 0x11, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, + 0x75, 0x69, 0x64, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x23, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x13, + 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, + 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, + 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x8b, 0x01, 0x0a, 0x24, 0x49, 0x6e, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x63, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, - 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x59, 0x0a, 0x15, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x22, 0x00, 0x28, 0x01, 0x12, 0x6f, 0x0a, 0x12, 0x52, 0x75, 0x6e, 0x53, 0x74, - 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x29, 0x2e, - 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, - 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, - 0x6b, 0x61, 0x67, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, - 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x26, 0x2e, 0x61, 0x70, - 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8d, 0x01, 0x0a, 0x2a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, - 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, - 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x45, 0x2e, 0x61, + 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x23, 0x46, 0x69, 0x6c, 0x65, 0x41, + 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, + 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x26, 0x0a, 0x0c, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, + 0x74, 0x65, 0x78, 0x74, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x88, 0x01, 0x01, 0x42, 0x0f, + 0x0a, 0x0d, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x22, + 0x4b, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x22, 0x19, 0x0a, 0x17, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x03, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, + 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x49, + 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x2b, + 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x69, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x70, + 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x12, 0x3a, 0x0a, + 0x1a, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, + 0x6f, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x16, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x54, + 0x6f, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x69, + 0x6e, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x61, 0x69, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x5b, 0x0a, 0x15, 0x65, 0x78, 0x70, 0x65, 0x72, + 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4b, 0x75, 0x72, 0x74, 0x6f, + 0x73, 0x69, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x14, + 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0b, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x78, 0x65, - 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x79, 0x0a, 0x22, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, - 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, - 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x39, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x61, 0x69, - 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, - 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x7b, 0x0a, - 0x23, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, - 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x79, 0x12, 0x3a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, - 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, 0x67, 0x73, - 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x13, 0x55, 0x70, + 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, + 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2a, 0x36, 0x0a, + 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, + 0x0a, 0x07, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x52, + 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, + 0x4f, 0x57, 0x4e, 0x10, 0x02, 0x2a, 0x2c, 0x0a, 0x11, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, + 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x10, 0x01, 0x2a, 0x26, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x0b, + 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, + 0x4f, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x01, 0x2a, 0x32, 0x0a, 0x13, 0x4b, + 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, + 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x17, 0x4e, 0x4f, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x52, 0x55, 0x43, + 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x2a, + 0x26, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x12, 0x09, 0x0a, 0x05, 0x4e, 0x45, 0x56, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, + 0x4c, 0x57, 0x41, 0x59, 0x53, 0x10, 0x01, 0x32, 0xce, 0x0e, 0x0a, 0x13, 0x41, 0x70, 0x69, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x6d, 0x0a, 0x11, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, + 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x2a, + 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, + 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x59, + 0x0a, 0x15, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, + 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x28, 0x01, 0x12, 0x6f, 0x0a, 0x12, 0x52, 0x75, 0x6e, + 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, + 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, + 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x0b, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x26, 0x2e, + 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, + 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8d, 0x01, 0x0a, 0x2a, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x45, + 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, + 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, + 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0b, 0x45, 0x78, 0x65, 0x63, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, + 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x79, 0x0a, 0x22, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, + 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x39, 0x2e, 0x61, 0x70, 0x69, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x57, + 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, + 0x7b, 0x0a, 0x23, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, + 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x3a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, + 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, + 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x13, + 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, + 0x61, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, + 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, - 0x74, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, - 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x6c, 0x6f, - 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x6f, 0x0a, 0x15, 0x44, - 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, - 0x66, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, - 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x41, 0x72, - 0x67, 0x73, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, - 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x79, 0x0a, 0x15, - 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, - 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x6f, 0x0a, + 0x15, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, + 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, + 0x41, 0x72, 0x67, 0x73, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, + 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x79, + 0x0a, 0x15, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, + 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, + 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, - 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x41, - 0x72, 0x67, 0x73, 0x1a, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1d, 0x53, 0x74, 0x6f, 0x72, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, - 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x2e, 0x61, 0x70, 0x69, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, + 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1d, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, - 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, - 0x38, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, - 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, - 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, 0x1e, 0x4c, - 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x12, 0x16, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x39, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, - 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x61, 0x70, + 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, - 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, - 0x73, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x55, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, - 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x52, 0x5a, 0x50, 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, 0x61, 0x70, 0x69, 0x2f, - 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x6b, 0x75, 0x72, 0x74, - 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x70, 0x63, 0x5f, 0x61, 0x70, - 0x69, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, + 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x72, 0x67, + 0x73, 0x1a, 0x38, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, + 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, + 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, + 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x12, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x39, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, + 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, + 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, + 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x61, 0x70, + 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, + 0x72, 0x67, 0x73, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x55, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, + 0x52, 0x75, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x29, 0x2e, 0x61, 0x70, + 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x52, 0x5a, 0x50, 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, 0x61, 0x70, + 0x69, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x6b, 0x75, + 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x70, 0x63, 0x5f, + 0x61, 0x70, 0x69, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/golang/core/lib/binding_constructors/binding_constructors.go b/api/golang/core/lib/binding_constructors/binding_constructors.go index 1a99cdae90..9a6d8bc25a 100644 --- a/api/golang/core/lib/binding_constructors/binding_constructors.go +++ b/api/golang/core/lib/binding_constructors/binding_constructors.go @@ -263,13 +263,14 @@ func NewStarlarkRunResponseLineFromRunSuccessEvent(serializedOutputObject string } } -func NewStarlarkInstruction(position *kurtosis_core_rpc_api_bindings.StarlarkInstructionPosition, name string, executableInstruction string, arguments []*kurtosis_core_rpc_api_bindings.StarlarkInstructionArg, isSkipped bool) *kurtosis_core_rpc_api_bindings.StarlarkInstruction { +func NewStarlarkInstruction(position *kurtosis_core_rpc_api_bindings.StarlarkInstructionPosition, name string, executableInstruction string, arguments []*kurtosis_core_rpc_api_bindings.StarlarkInstructionArg, isSkipped bool, description string) *kurtosis_core_rpc_api_bindings.StarlarkInstruction { return &kurtosis_core_rpc_api_bindings.StarlarkInstruction{ InstructionName: name, Position: position, ExecutableInstruction: executableInstruction, Arguments: arguments, IsSkipped: isSkipped, + Description: description, } } diff --git a/api/protobuf/core/api_container_service.proto b/api/protobuf/core/api_container_service.proto index 170f323d97..9e53fa965b 100644 --- a/api/protobuf/core/api_container_service.proto +++ b/api/protobuf/core/api_container_service.proto @@ -256,6 +256,8 @@ message StarlarkInstruction { string executable_instruction = 4; bool is_skipped = 5; + + string description = 6; } message StarlarkInstructionResult { diff --git a/api/rust/src/api_container_api.rs b/api/rust/src/api_container_api.rs index 62b55c7679..dbbb6c6480 100644 --- a/api/rust/src/api_container_api.rs +++ b/api/rust/src/api_container_api.rs @@ -308,6 +308,8 @@ pub struct StarlarkInstruction { pub executable_instruction: ::prost::alloc::string::String, #[prost(bool, tag = "5")] pub is_skipped: bool, + #[prost(string, tag = "6")] + pub description: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts index bff0c23421..d4d830a5e7 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts @@ -502,6 +502,9 @@ export class StarlarkInstruction extends jspb.Message { getIsSkipped(): boolean; setIsSkipped(value: boolean): StarlarkInstruction; + getDescription(): string; + setDescription(value: string): StarlarkInstruction; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): StarlarkInstruction.AsObject; static toObject(includeInstance: boolean, msg: StarlarkInstruction): StarlarkInstruction.AsObject; @@ -517,6 +520,7 @@ export namespace StarlarkInstruction { argumentsList: Array, executableInstruction: string, isSkipped: boolean, + description: string, } } diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.js b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.js index 9663e05288..94eef3a39c 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.js +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.js @@ -3972,7 +3972,8 @@ proto.api_container_api.StarlarkInstruction.toObject = function(includeInstance, argumentsList: jspb.Message.toObjectList(msg.getArgumentsList(), proto.api_container_api.StarlarkInstructionArg.toObject, includeInstance), executableInstruction: jspb.Message.getFieldWithDefault(msg, 4, ""), - isSkipped: jspb.Message.getBooleanFieldWithDefault(msg, 5, false) + isSkipped: jspb.Message.getBooleanFieldWithDefault(msg, 5, false), + description: jspb.Message.getFieldWithDefault(msg, 6, "") }; if (includeInstance) { @@ -4031,6 +4032,10 @@ proto.api_container_api.StarlarkInstruction.deserializeBinaryFromReader = functi var value = /** @type {boolean} */ (reader.readBool()); msg.setIsSkipped(value); break; + case 6: + var value = /** @type {string} */ (reader.readString()); + msg.setDescription(value); + break; default: reader.skipField(); break; @@ -4097,6 +4102,13 @@ proto.api_container_api.StarlarkInstruction.serializeBinaryToWriter = function(m f ); } + f = message.getDescription(); + if (f.length > 0) { + writer.writeString( + 6, + f + ); + } }; @@ -4229,6 +4241,24 @@ proto.api_container_api.StarlarkInstruction.prototype.setIsSkipped = function(va }; +/** + * optional string description = 6; + * @return {string} + */ +proto.api_container_api.StarlarkInstruction.prototype.getDescription = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); +}; + + +/** + * @param {string} value + * @return {!proto.api_container_api.StarlarkInstruction} returns this + */ +proto.api_container_api.StarlarkInstruction.prototype.setDescription = function(value) { + return jspb.Message.setProto3StringField(this, 6, value); +}; + + diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts index bbef713a48..f230ed1e42 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts @@ -653,6 +653,11 @@ export declare class StarlarkInstruction extends Message { */ isSkipped: boolean; + /** + * @generated from field: string description = 6; + */ + description: string; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js index eed1454734..6aeefe438c 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js @@ -226,6 +226,7 @@ export const StarlarkInstruction = proto3.makeMessageType( { no: 3, name: "arguments", kind: "message", T: StarlarkInstructionArg, repeated: true }, { no: 4, name: "executable_instruction", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 5, name: "is_skipped", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + { no: 6, name: "description", kind: "scalar", T: 9 /* ScalarType.STRING */ }, ], ); diff --git a/cli/cli/command_args/run/verbosity.go b/cli/cli/command_args/run/verbosity.go index 2eb4679b9f..91d6dd374a 100644 --- a/cli/cli/command_args/run/verbosity.go +++ b/cli/cli/command_args/run/verbosity.go @@ -8,4 +8,5 @@ const ( Detailed Executable OutputOnly + Description ) diff --git a/cli/cli/command_args/run/verbosity_enumer.go b/cli/cli/command_args/run/verbosity_enumer.go index 5bfb4c6b2e..4b6251b10d 100644 --- a/cli/cli/command_args/run/verbosity_enumer.go +++ b/cli/cli/command_args/run/verbosity_enumer.go @@ -7,11 +7,11 @@ import ( "strings" ) -const _VerbosityName = "BRIEFDETAILEDEXECUTABLEOUTPUT_ONLY" +const _VerbosityName = "BRIEFDETAILEDEXECUTABLEOUTPUT_ONLYDESCRIPTION" -var _VerbosityIndex = [...]uint8{0, 5, 13, 23, 34} +var _VerbosityIndex = [...]uint8{0, 5, 13, 23, 34, 45} -const _VerbosityLowerName = "briefdetailedexecutableoutput_only" +const _VerbosityLowerName = "briefdetailedexecutableoutput_onlydescription" func (i Verbosity) String() string { if i < 0 || i >= Verbosity(len(_VerbosityIndex)-1) { @@ -28,9 +28,10 @@ func _VerbosityNoOp() { _ = x[Detailed-(1)] _ = x[Executable-(2)] _ = x[OutputOnly-(3)] + _ = x[Description-(4)] } -var _VerbosityValues = []Verbosity{Brief, Detailed, Executable, OutputOnly} +var _VerbosityValues = []Verbosity{Brief, Detailed, Executable, OutputOnly, Description} var _VerbosityNameToValueMap = map[string]Verbosity{ _VerbosityName[0:5]: Brief, @@ -41,6 +42,8 @@ var _VerbosityNameToValueMap = map[string]Verbosity{ _VerbosityLowerName[13:23]: Executable, _VerbosityName[23:34]: OutputOnly, _VerbosityLowerName[23:34]: OutputOnly, + _VerbosityName[34:45]: Description, + _VerbosityLowerName[34:45]: Description, } var _VerbosityNames = []string{ @@ -48,6 +51,7 @@ var _VerbosityNames = []string{ _VerbosityName[5:13], _VerbosityName[13:23], _VerbosityName[23:34], + _VerbosityName[34:45], } // VerbosityString retrieves an enum value from the enum constants string name. diff --git a/cli/cli/commands/run/run.go b/cli/cli/commands/run/run.go index 023d20082f..9d3bc3d3e0 100644 --- a/cli/cli/commands/run/run.go +++ b/cli/cli/commands/run/run.go @@ -140,7 +140,7 @@ var StarlarkRunCmd = &engine_consuming_kurtosis_command.EngineConsumingKurtosisC }, { Key: verbosityFlagKey, - Usage: fmt.Sprintf("The verbosity of the command output: %s. If unset, it defaults to `brief` for a concise and explicit output. Use `detailed` to display the exhaustive list of arguments for each command. `executable` will generate executable Starlark instructions.", strings.Join(command_args_run.VerbosityStrings(), ", ")), + Usage: fmt.Sprintf("The verbosity of the command output: %s. If unset, it defaults to `brief` for a concise and explicit output. Use `detailed` to display the exhaustive list of arguments for each command. `executable` will generate executable Starlark instructions. `description` will just print a description of what is about to happen without any details", strings.Join(command_args_run.VerbosityStrings(), ", ")), Type: flags.FlagType_String, Shorthand: "v", Default: defaultVerbosity, diff --git a/cli/cli/helpers/output_printers/kurtosis_instruction_printer.go b/cli/cli/helpers/output_printers/kurtosis_instruction_printer.go index 465d97e790..6fe307e23a 100644 --- a/cli/cli/helpers/output_printers/kurtosis_instruction_printer.go +++ b/cli/cli/helpers/output_printers/kurtosis_instruction_printer.go @@ -189,6 +189,8 @@ func formatInfo(infoMessage string) string { func formatInstruction(instruction *kurtosis_core_rpc_api_bindings.StarlarkInstruction, verbosity run.Verbosity) string { var serializedInstruction string switch verbosity { + case run.Description: + serializedInstruction = instruction.Description case run.Brief: serializedInstruction = formatInstructionToReadableString(instruction, false) case run.Detailed: diff --git a/cli/cli/helpers/output_printers/kurtosis_instruction_printer_test.go b/cli/cli/helpers/output_printers/kurtosis_instruction_printer_test.go index 82c2f95eb7..4660c206ee 100644 --- a/cli/cli/helpers/output_printers/kurtosis_instruction_printer_test.go +++ b/cli/cli/helpers/output_printers/kurtosis_instruction_printer_test.go @@ -27,7 +27,8 @@ func testInstruction() *kurtosis_core_rpc_api_bindings.StarlarkInstruction { binding_constructors.NewStarlarkInstructionKwarg("serviceA", "kwarg1", true), binding_constructors.NewStarlarkInstructionKwarg(`struct(bonjour=42, hello="world")`, "kwarg2", false), }, - isSkipped) + isSkipped, + "description") } func TestFormatInstruction_Executable(t *testing.T) { @@ -74,7 +75,8 @@ func TestFormatInstruction_FormattingFail(t *testing.T) { // This has issues with the quotes not being escaped `print("UNSUPPORTED_TYPE['ModuleOutput(grafana_info=GrafanaInfo(dashboard_path="/d/QdTOwy-nz/eth2-merge-kurtosis-module-dashboard?orgId=1", user="admin", password="admin"))']")`, []*kurtosis_core_rpc_api_bindings.StarlarkInstructionArg{}, - isSkipped) + isSkipped, + "description") formattedInstruction := formatInstruction(instruction, run.Executable) // failure to format -> the instruction is returned with no formatting applied expectedResult := `# from dummyFile[12:4] diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service.go index f948d3ce71..a3686e9a6d 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service.go @@ -231,6 +231,10 @@ func (builtin *AddServiceCapabilities) FillPersistableAttributes(builder *enclav ) } +func (builtin *AddServiceCapabilities) Description() string { + return fmt.Sprintf("Adding service with name '%v' and image '%v'", builtin.serviceName, builtin.serviceConfig.GetContainerImageName()) +} + func validateAndConvertConfigAndReadyCondition( serviceNetwork service_network.ServiceNetwork, rawConfig starlark.Value, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_services.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_services.go index f5c2f372c0..a99eaa6ee2 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_services.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_services.go @@ -353,6 +353,19 @@ func (builtin *AddServicesCapabilities) allServicesReadinessCheck( return failedServiceChecksRegularMap } +func (builtin *AddServicesCapabilities) Description() string { + return fmt.Sprintf("Adding '%v' services with names '%v'", len(builtin.serviceConfigs), getNamesAsCommaSeparatedList(builtin.serviceConfigs)) +} + +func getNamesAsCommaSeparatedList(serviceConfigs map[service.ServiceName]*service.ServiceConfig) string { + var serviceNames []string + serviceNameSeparator := "," + for serviceName := range serviceConfigs { + serviceNames = append(serviceNames, string(serviceName)) + } + return strings.Join(serviceNames, serviceNameSeparator) +} + func (builtin *AddServicesCapabilities) runServiceReadinessCheck( ctx context.Context, wg *sync.WaitGroup, diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/exec/exec.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/exec/exec.go index 7f95de1e03..312423550b 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/exec/exec.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/exec/exec.go @@ -189,6 +189,10 @@ func (builtin *ExecCapabilities) FillPersistableAttributes(builder *enclave_plan builder.SetType(ExecBuiltinName) } +func (builtin *ExecCapabilities) Description() string { + return fmt.Sprintf("Executing command on service '%v'", builtin.serviceName) +} + func (builtin *ExecCapabilities) isAcceptableCode(recipeResult map[string]starlark.Comparable) bool { isAcceptableCode := false for _, acceptableCode := range builtin.acceptableCodes { diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_print/kurtosis_print.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_print/kurtosis_print.go index ee70b3db91..c76f713e3b 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_print/kurtosis_print.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/kurtosis_print/kurtosis_print.go @@ -99,3 +99,7 @@ func (builtin *PrintCapabilities) TryResolveWith(instructionsAreEqual bool, _ *e func (builtin *PrintCapabilities) FillPersistableAttributes(builder *enclave_plan_persistence.EnclavePlanInstructionBuilder) { builder.SetType(PrintBuiltinName) } + +func (builtin *PrintCapabilities) Description() string { + return "Printing a message" +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service/remove_service.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service/remove_service.go index 153b4d9735..6285791e2d 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service/remove_service.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/remove_service/remove_service.go @@ -101,3 +101,7 @@ func (builtin *RemoveServiceCapabilities) FillPersistableAttributes(builder *enc builtin.serviceName, ) } + +func (builtin *RemoveServiceCapabilities) Description() string { + return fmt.Sprintf("Removing service '%v'", builtin.serviceName) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/render_templates/render_templates.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/render_templates/render_templates.go index 6277148eb0..7fa2138c51 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/render_templates/render_templates.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/render_templates/render_templates.go @@ -168,6 +168,10 @@ func (builtin *RenderTemplatesCapabilities) FillPersistableAttributes(builder *e ) } +func (builtin *RenderTemplatesCapabilities) Description() string { + return fmt.Sprintf("Rendering a template to a files artifact with name '%v'", builtin.artifactName) +} + func parseTemplatesAndData(templatesAndData *starlark.Dict) (map[string]*render_templates.TemplateData, *startosis_errors.InterpretationError) { templateAndDataByDestRelFilepath := make(map[string]*render_templates.TemplateData) for _, relPathInFilesArtifactKey := range templatesAndData.Keys() { diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/request/request.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/request/request.go index 15ed20994c..22061db92c 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/request/request.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/request/request.go @@ -2,6 +2,7 @@ package request import ( "context" + "fmt" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_plan_persistence" @@ -201,6 +202,10 @@ func (builtin *RequestCapabilities) FillPersistableAttributes(builder *enclave_p builder.SetType(RequestBuiltinName) } +func (builtin *RequestCapabilities) Description() string { + return fmt.Sprintf("Running '%v' request on service '%v'", builtin.httpRequestRecipe.RequestType(), builtin.serviceName) +} + func (builtin *RequestCapabilities) isAcceptableCode(recipeResult map[string]starlark.Comparable) bool { isAcceptableCode := false for _, acceptableCode := range builtin.acceptableCodes { diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/start_service/start_service.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/start_service/start_service.go index 9aa6ecc24b..f5e40901c5 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/start_service/start_service.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/start_service/start_service.go @@ -97,3 +97,7 @@ func (builtin *StartServiceCapabilities) FillPersistableAttributes(builder *encl builtin.serviceName, ) } + +func (builtin *StartServiceCapabilities) Description() string { + return fmt.Sprintf("Starting service '%v'", builtin.serviceName) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/stop_service/stop_service.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/stop_service/stop_service.go index 13f8dba3d6..7ca91287d8 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/stop_service/stop_service.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/stop_service/stop_service.go @@ -97,3 +97,7 @@ func (builtin *StopServiceCapabilities) FillPersistableAttributes(builder *encla builtin.serviceName, ) } + +func (builtin *StopServiceCapabilities) Description() string { + return fmt.Sprintf("Stopping service '%v'", builtin.serviceName) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/store_service_files/store_service_files.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/store_service_files/store_service_files.go index c180717ae8..dac882f296 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/store_service_files/store_service_files.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/store_service_files/store_service_files.go @@ -171,3 +171,7 @@ func (builtin *StoreServiceFilesCapabilities) FillPersistableAttributes(builder builtin.artifactName, nil, ) } + +func (builtin *StoreServiceFilesCapabilities) Description() string { + return fmt.Sprintf("Storing files from service '%v' at path '%v' to files artifact with name '%v'", builtin.serviceName, builtin.src, builtin.artifactName) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go index 9633e36c51..5e500f2eac 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go @@ -321,6 +321,10 @@ func (builtin *RunPythonCapabilities) FillPersistableAttributes(builder *enclave builder.SetType(RunPythonBuiltinName) } +func (builtin *RunPythonCapabilities) Description() string { + return "Running a one time python script" +} + func setupRequiredPackages(ctx context.Context, builtin *RunPythonCapabilities) (*exec_result.ExecResult, error) { if len(builtin.packages) == 0 { return nil, nil diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go index d73e062c35..91e4ff12fb 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go @@ -266,6 +266,10 @@ func (builtin *RunShCapabilities) FillPersistableAttributes(builder *enclave_pla builder.SetType(RunShBuiltinName) } +func (builtin *RunShCapabilities) Description() string { + return "Running a one time bash script" +} + func getCommandToRun(builtin *RunShCapabilities) (string, error) { // replace future references to actual strings maybeSubCommandWithRuntimeValues, err := magic_string_helper.ReplaceRuntimeValueInString(builtin.run, builtin.runtimeValueStore) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go index cf2135e225..8c5a9e932a 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go @@ -213,3 +213,7 @@ func (builtin *UploadFilesCapabilities) FillPersistableAttributes(builder *encla builtin.artifactName, builtin.filesArtifactMd5, ) } + +func (builtin *UploadFilesCapabilities) Description() string { + return fmt.Sprintf("Uploading file '%v' to files articact '%v'", builtin.src, builtin.artifactName) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/verify/verify.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/verify/verify.go index abeab09a6f..9670160037 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/verify/verify.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/verify/verify.go @@ -155,6 +155,10 @@ func (builtin *VerifyCapabilities) FillPersistableAttributes(builder *enclave_pl builder.SetType(VerifyBuiltinName) } +func (builtin *VerifyCapabilities) Description() string { + return fmt.Sprintf("Verifying whether two values meet a certain condition '%v'", builtin.assertion) +} + // Verify verifies whether the currentValue matches the targetValue w.r.t. the assertion operator // TODO: This and ValidateVerificationToken below are used by both verify and wait. Refactor it to a better place func Verify(currentValue starlark.Comparable, assertion string, targetValue starlark.Comparable) error { diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/wait/wait.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/wait/wait.go index 4c6f228c6a..7de94826fc 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/wait/wait.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/wait/wait.go @@ -288,3 +288,7 @@ func (builtin *WaitCapabilities) TryResolveWith(instructionsAreEqual bool, _ *en func (builtin *WaitCapabilities) FillPersistableAttributes(builder *enclave_plan_persistence.EnclavePlanInstructionBuilder) { builder.SetType(WaitBuiltinName) } + +func (builtin *WaitCapabilities) Description() string { + return fmt.Sprintf("Waiting for at most '%v' for service '%v' to reach a certain state", builtin.timeout, builtin.serviceName) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_capabilities.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_capabilities.go index a96e12a9f8..66ca407c64 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_capabilities.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_capabilities.go @@ -22,4 +22,7 @@ type KurtosisPlanInstructionCapabilities interface { // FillPersistableAttributes adds to the builder the attributes of the instruction that needs to be persisted to the // enclave database to power idempotent runs. FillPersistableAttributes(builder *enclave_plan_persistence.EnclavePlanInstructionBuilder) + + // Description Brief description of the instruction based on its contents + Description() string } diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_internal.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_internal.go index 0db17f3950..e05a718083 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_internal.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/kurtosis_plan_instruction/kurtosis_plan_instruction_internal.go @@ -46,7 +46,7 @@ func (builtin *kurtosisPlanInstructionInternal) GetCanonicalInstruction(isSkippe } args[idx] = binding_constructors.NewStarlarkInstructionKwarg(builtin_argument.StringifyArgumentValue(value), name, shouldBeDisplayed) } - return binding_constructors.NewStarlarkInstruction(builtin.GetPosition().ToAPIType(), builtin.GetName(), builtin.String(), args, isSkipped) + return binding_constructors.NewStarlarkInstruction(builtin.GetPosition().ToAPIType(), builtin.GetName(), builtin.String(), args, isSkipped, builtin.capabilities.Description()) } // GetPositionInOriginalScript is here to implement the KurtosisInstruction interface. Remove it when it's not needed anymore diff --git a/core/server/api_container/server/startosis_engine/startosis_executor_test.go b/core/server/api_container/server/startosis_engine/startosis_executor_test.go index 8da1fa06d7..be3b507256 100644 --- a/core/server/api_container/server/startosis_engine/startosis_executor_test.go +++ b/core/server/api_container/server/startosis_engine/startosis_executor_test.go @@ -52,12 +52,12 @@ func TestExecuteKurtosisInstructions_ExecuteForReal_Success(t *testing.T) { executor := NewStartosisExecutor(nil, runtimeValueStore, enclave_plan_persistence.NewEnclavePlan(), enclaveDb) instructionsPlan := instructions_plan.NewInstructionsPlan() - instruction1 := createMockInstruction(t, "instruction1", executeSuccessfully) + instruction1 := createMockInstruction(t, "instruction1", executeSuccessfully, "description1") scheduledInstruction1 := instructions_plan.NewScheduledInstruction("instruction1", instruction1, starlark.None).Executed(true) instructionsPlan.AddScheduledInstruction(scheduledInstruction1) - instruction2 := createMockInstruction(t, "instruction2", executeSuccessfully) - instruction3 := createMockInstruction(t, "instruction3", executeSuccessfully) + instruction2 := createMockInstruction(t, "instruction2", executeSuccessfully, "description2") + instruction3 := createMockInstruction(t, "instruction3", executeSuccessfully, "description3") require.NoError(t, instructionsPlan.AddInstruction(instruction2, starlark.None)) require.NoError(t, instructionsPlan.AddInstruction(instruction3, starlark.None)) @@ -75,11 +75,11 @@ func TestExecuteKurtosisInstructions_ExecuteForReal_Success(t *testing.T) { expectedSerializedInstructions := []*kurtosis_core_rpc_api_bindings.StarlarkInstruction{ binding_constructors.NewStarlarkInstruction( - dummyPosition.ToAPIType(), "instruction1", "instruction1()", noInstructionArgsForTesting, isSkipped), + dummyPosition.ToAPIType(), "instruction1", "instruction1()", noInstructionArgsForTesting, isSkipped, "description1"), binding_constructors.NewStarlarkInstruction( - dummyPosition.ToAPIType(), "instruction2", "instruction2()", noInstructionArgsForTesting, isSkipped), + dummyPosition.ToAPIType(), "instruction2", "instruction2()", noInstructionArgsForTesting, isSkipped, "description2"), binding_constructors.NewStarlarkInstruction( - dummyPosition.ToAPIType(), "instruction3", "instruction3()", noInstructionArgsForTesting, isSkipped), + dummyPosition.ToAPIType(), "instruction3", "instruction3()", noInstructionArgsForTesting, isSkipped, "description3"), } require.Equal(t, expectedSerializedInstructions, serializedInstruction) require.Equal(t, executor.enclavePlan.Size(), 3) // check that the enclave plan now contains the 4 instructions @@ -95,9 +95,9 @@ func TestExecuteKurtosisInstructions_ExecuteForReal_FailureHalfWay(t *testing.T) executor := NewStartosisExecutor(nil, runtimeValueStore, enclave_plan_persistence.NewEnclavePlan(), enclaveDb) - instruction1 := createMockInstruction(t, "instruction1", executeSuccessfully) - instruction2 := createMockInstruction(t, "instruction2", throwOnExecute) - instruction3 := createMockInstruction(t, "instruction3", executeSuccessfully) + instruction1 := createMockInstruction(t, "instruction1", executeSuccessfully, "description1") + instruction2 := createMockInstruction(t, "instruction2", throwOnExecute, "description2") + instruction3 := createMockInstruction(t, "instruction3", executeSuccessfully, "description3") instructionsPlan := instructions_plan.NewInstructionsPlan() require.NoError(t, instructionsPlan.AddInstruction(instruction1, starlark.None)) require.NoError(t, instructionsPlan.AddInstruction(instruction2, starlark.None)) @@ -123,9 +123,9 @@ instruction2() expectedSerializedInstructions := []*kurtosis_core_rpc_api_bindings.StarlarkInstruction{ // only instruction 1 and 2 because it failed at instruction 2 binding_constructors.NewStarlarkInstruction( - dummyPosition.ToAPIType(), "instruction1", "instruction1()", noInstructionArgsForTesting, isSkipped), + dummyPosition.ToAPIType(), "instruction1", "instruction1()", noInstructionArgsForTesting, isSkipped, "description1"), binding_constructors.NewStarlarkInstruction( - dummyPosition.ToAPIType(), "instruction2", "instruction2()", noInstructionArgsForTesting, isSkipped), + dummyPosition.ToAPIType(), "instruction2", "instruction2()", noInstructionArgsForTesting, isSkipped, "description2"), } require.Equal(t, expectedSerializedInstructions, serializedInstruction) } @@ -140,8 +140,8 @@ func TestExecuteKurtosisInstructions_DoDryRun(t *testing.T) { executor := NewStartosisExecutor(nil, runtimeValueStore, enclave_plan_persistence.NewEnclavePlan(), enclaveDb) - instruction1 := createMockInstruction(t, "instruction1", executeSuccessfully) - instruction2 := createMockInstruction(t, "instruction2", executeSuccessfully) + instruction1 := createMockInstruction(t, "instruction1", executeSuccessfully, "description1") + instruction2 := createMockInstruction(t, "instruction2", executeSuccessfully, "description2") instructionsPlan := instructions_plan.NewInstructionsPlan() require.NoError(t, instructionsPlan.AddInstruction(instruction1, starlark.None)) require.NoError(t, instructionsPlan.AddInstruction(instruction2, starlark.None)) @@ -157,19 +157,19 @@ func TestExecuteKurtosisInstructions_DoDryRun(t *testing.T) { expectedSerializedInstructions := []*kurtosis_core_rpc_api_bindings.StarlarkInstruction{ binding_constructors.NewStarlarkInstruction( - dummyPosition.ToAPIType(), "instruction1", "instruction1()", noInstructionArgsForTesting, isSkipped), + dummyPosition.ToAPIType(), "instruction1", "instruction1()", noInstructionArgsForTesting, isSkipped, "description1"), binding_constructors.NewStarlarkInstruction( - dummyPosition.ToAPIType(), "instruction2", "instruction2()", noInstructionArgsForTesting, isSkipped), + dummyPosition.ToAPIType(), "instruction2", "instruction2()", noInstructionArgsForTesting, isSkipped, "description2"), } require.Equal(t, serializedInstruction, expectedSerializedInstructions) } -func createMockInstruction(t *testing.T, instructionName string, executeSuccessfully bool) *mock_instruction.MockKurtosisInstruction { +func createMockInstruction(t *testing.T, instructionName string, executeSuccessfully bool, description string) *mock_instruction.MockKurtosisInstruction { instruction := mock_instruction.NewMockKurtosisInstruction(t) stringifiedInstruction := instructionName + "()" canonicalInstruction := binding_constructors.NewStarlarkInstruction( - dummyPosition.ToAPIType(), instructionName, stringifiedInstruction, noInstructionArgsForTesting, isSkipped) + dummyPosition.ToAPIType(), instructionName, stringifiedInstruction, noInstructionArgsForTesting, isSkipped, description) instruction.EXPECT().GetCanonicalInstruction(mock.Anything).Maybe().Return(canonicalInstruction) instruction.EXPECT().GetPositionInOriginalScript().Maybe().Return(dummyPosition) instruction.EXPECT().String().Maybe().Return(stringifiedInstruction) diff --git a/package-lock.json b/package-lock.json index 4d44aba012..12baf66b21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,12 @@ "requires": true, "packages": { "": { + "dependencies": { + "@bufbuild/buf": "^1.29.0", + "@bufbuild/protobuf": "^1.7.2", + "@bufbuild/protoc-gen-es": "^1.7.2", + "openapi-typescript": "7.0.0-next.5" + }, "devDependencies": { "@tailwindcss/typography": "^0.5.9" } @@ -21,6 +27,156 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@bufbuild/buf": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.29.0.tgz", + "integrity": "sha512-euksXeFtvlvAV5j94LqXb69qQcJvFfo8vN1d3cx+IzhOKoipykuQQTq7mOWVo2R0kdk6yIMBLBofOYOsh0Df8g==", + "hasInstallScript": true, + "bin": { + "buf": "bin/buf", + "protoc-gen-buf-breaking": "bin/protoc-gen-buf-breaking", + "protoc-gen-buf-lint": "bin/protoc-gen-buf-lint" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@bufbuild/buf-darwin-arm64": "1.29.0", + "@bufbuild/buf-darwin-x64": "1.29.0", + "@bufbuild/buf-linux-aarch64": "1.29.0", + "@bufbuild/buf-linux-x64": "1.29.0", + "@bufbuild/buf-win32-arm64": "1.29.0", + "@bufbuild/buf-win32-x64": "1.29.0" + } + }, + "node_modules/@bufbuild/buf-darwin-arm64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.29.0.tgz", + "integrity": "sha512-5hKxsARoY2WpWq1n5ONFqqGuauHb4yILKXCy37KRYCKiRLWmIP5yI3gWvWHKoH7sUJWTQmBqdJoCvYQr6ahQnw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-darwin-x64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.29.0.tgz", + "integrity": "sha512-wOAPxbPLBns4AHiComWtdO1sx1J1p6mDYTbqmloHuI+B5U2rDbMsoHoe4nBcoMF8+RHxoqjypha29wVo6yzbZg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-aarch64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.29.0.tgz", + "integrity": "sha512-jLk2J/wyyM7KNJ/DkLfhy3eS2/Bdb70e/56adMkapSoLJmghnpgxW+oFznMxxQUX5I9BU5hTn1UhDFxgLwhP7g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-x64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.29.0.tgz", + "integrity": "sha512-heLOywj3Oaoh69RnTx7tHsuz6rEnvz77bghLEOghsrjBR6Jcpcwc137EZR4kRTIWJNrE8Kmo3RVeXlv144qQIQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-win32-arm64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.29.0.tgz", + "integrity": "sha512-Eglyvr3PLqVucuHBcQ61conyBgH9BRaoLpKWcce1gYBVlxMQM1NxjVjGOWihxQ1dXXw5qZXmYfVODf3gSwPMuQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-win32-x64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.29.0.tgz", + "integrity": "sha512-wRk6co+nqHqEq4iLolXgej0jUVlWlTtGHjKaq54lTbKZrwxrBgql6qS06abgNPRASX0++XT9m3QRZ97qEIC/HQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.7.2.tgz", + "integrity": "sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ==" + }, + "node_modules/@bufbuild/protoc-gen-es": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.7.2.tgz", + "integrity": "sha512-yiRk/T+YGmpSVvIkybCjPt+QyM/pLWMO+MAiz6auvCsiAgfXfc5nFFosD4yBYXID55M6eIkgBcity1AoJ6I30A==", + "dependencies": { + "@bufbuild/protobuf": "^1.7.2", + "@bufbuild/protoplugin": "1.7.2" + }, + "bin": { + "protoc-gen-es": "bin/protoc-gen-es" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@bufbuild/protobuf": "1.7.2" + }, + "peerDependenciesMeta": { + "@bufbuild/protobuf": { + "optional": true + } + } + }, + "node_modules/@bufbuild/protoplugin": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.7.2.tgz", + "integrity": "sha512-N3QtO8XWD4F4alMtASWtxBw6BWXp4aLz7rPBXH4KTULdjpUHnq46g15TsrG0/8szZw6pIklTO3lFe14dl6ZYdA==", + "dependencies": { + "@bufbuild/protobuf": "1.7.2", + "@typescript/vfs": "^1.4.0", + "typescript": "4.5.2" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -112,6 +268,60 @@ "node": ">= 8" } }, + "node_modules/@redocly/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@redocly/openapi-core": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.8.2.tgz", + "integrity": "sha512-VjUz3wrqcDbO1HfEB0AUzh6Y7T1jNJR4Jmgfs0ipuoipLjU5bDsdfKJGSSz2u0WpfmqklPsd11ynkgL5Y+MlCg==", + "dependencies": { + "@redocly/ajv": "^8.11.0", + "colorette": "^1.2.0", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "lodash.isequal": "^4.5.0", + "minimatch": "^5.0.1", + "node-fetch": "^2.6.1", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=14.19.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@redocly/openapi-core/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@redocly/openapi-core/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@tailwindcss/typography": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz", @@ -127,6 +337,22 @@ "tailwindcss": ">=3.0.0 || insiders" } }, + "node_modules/@typescript/vfs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz", + "integrity": "sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -155,12 +381,15 @@ "dev": true, "peer": true }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "peer": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -247,6 +476,11 @@ "node": ">= 6" } }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -276,6 +510,22 @@ "node": ">=4" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -290,6 +540,11 @@ "dev": true, "peer": true }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -506,6 +761,30 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -529,6 +808,11 @@ "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", "dev": true }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -578,6 +862,11 @@ "node": "*" } }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -609,6 +898,25 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -649,6 +957,33 @@ "wrappy": "1" } }, + "node_modules/openapi-typescript": { + "version": "7.0.0-next.5", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.0.0-next.5.tgz", + "integrity": "sha512-zqEDw/FZkT0ndOCd8EybkDVwEYgaOh+ryWm6OCON70DmY9YqUnNSIVyRFVjN8hesa0bxOs9QOMzXAasczNdHbQ==", + "dependencies": { + "@redocly/openapi-core": "^1.4.1", + "ansi-colors": "^4.1.3", + "supports-color": "^9.4.0", + "typescript": "^5.3.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + } + }, + "node_modules/openapi-typescript/node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -706,6 +1041,14 @@ "node": ">= 6" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "engines": { + "node": ">=4" + } + }, "node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -857,6 +1200,14 @@ "dev": true, "peer": true }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -901,6 +1252,14 @@ "node": ">=8.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", @@ -987,6 +1346,17 @@ "node": ">=8" } }, + "node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -1088,6 +1458,11 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -1095,12 +1470,46 @@ "dev": true, "peer": true }, + "node_modules/typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -1117,6 +1526,19 @@ "engines": { "node": ">= 14" } + }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==" + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } } }, "dependencies": { @@ -1127,6 +1549,79 @@ "dev": true, "peer": true }, + "@bufbuild/buf": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.29.0.tgz", + "integrity": "sha512-euksXeFtvlvAV5j94LqXb69qQcJvFfo8vN1d3cx+IzhOKoipykuQQTq7mOWVo2R0kdk6yIMBLBofOYOsh0Df8g==", + "requires": { + "@bufbuild/buf-darwin-arm64": "1.29.0", + "@bufbuild/buf-darwin-x64": "1.29.0", + "@bufbuild/buf-linux-aarch64": "1.29.0", + "@bufbuild/buf-linux-x64": "1.29.0", + "@bufbuild/buf-win32-arm64": "1.29.0", + "@bufbuild/buf-win32-x64": "1.29.0" + } + }, + "@bufbuild/buf-darwin-arm64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.29.0.tgz", + "integrity": "sha512-5hKxsARoY2WpWq1n5ONFqqGuauHb4yILKXCy37KRYCKiRLWmIP5yI3gWvWHKoH7sUJWTQmBqdJoCvYQr6ahQnw==", + "optional": true + }, + "@bufbuild/buf-darwin-x64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.29.0.tgz", + "integrity": "sha512-wOAPxbPLBns4AHiComWtdO1sx1J1p6mDYTbqmloHuI+B5U2rDbMsoHoe4nBcoMF8+RHxoqjypha29wVo6yzbZg==", + "optional": true + }, + "@bufbuild/buf-linux-aarch64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.29.0.tgz", + "integrity": "sha512-jLk2J/wyyM7KNJ/DkLfhy3eS2/Bdb70e/56adMkapSoLJmghnpgxW+oFznMxxQUX5I9BU5hTn1UhDFxgLwhP7g==", + "optional": true + }, + "@bufbuild/buf-linux-x64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.29.0.tgz", + "integrity": "sha512-heLOywj3Oaoh69RnTx7tHsuz6rEnvz77bghLEOghsrjBR6Jcpcwc137EZR4kRTIWJNrE8Kmo3RVeXlv144qQIQ==", + "optional": true + }, + "@bufbuild/buf-win32-arm64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.29.0.tgz", + "integrity": "sha512-Eglyvr3PLqVucuHBcQ61conyBgH9BRaoLpKWcce1gYBVlxMQM1NxjVjGOWihxQ1dXXw5qZXmYfVODf3gSwPMuQ==", + "optional": true + }, + "@bufbuild/buf-win32-x64": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.29.0.tgz", + "integrity": "sha512-wRk6co+nqHqEq4iLolXgej0jUVlWlTtGHjKaq54lTbKZrwxrBgql6qS06abgNPRASX0++XT9m3QRZ97qEIC/HQ==", + "optional": true + }, + "@bufbuild/protobuf": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.7.2.tgz", + "integrity": "sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ==" + }, + "@bufbuild/protoc-gen-es": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.7.2.tgz", + "integrity": "sha512-yiRk/T+YGmpSVvIkybCjPt+QyM/pLWMO+MAiz6auvCsiAgfXfc5nFFosD4yBYXID55M6eIkgBcity1AoJ6I30A==", + "requires": { + "@bufbuild/protobuf": "^1.7.2", + "@bufbuild/protoplugin": "1.7.2" + } + }, + "@bufbuild/protoplugin": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.7.2.tgz", + "integrity": "sha512-N3QtO8XWD4F4alMtASWtxBw6BWXp4aLz7rPBXH4KTULdjpUHnq46g15TsrG0/8szZw6pIklTO3lFe14dl6ZYdA==", + "requires": { + "@bufbuild/protobuf": "1.7.2", + "@typescript/vfs": "^1.4.0", + "typescript": "4.5.2" + } + }, "@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -1200,6 +1695,51 @@ "fastq": "^1.6.0" } }, + "@redocly/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "@redocly/openapi-core": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.8.2.tgz", + "integrity": "sha512-VjUz3wrqcDbO1HfEB0AUzh6Y7T1jNJR4Jmgfs0ipuoipLjU5bDsdfKJGSSz2u0WpfmqklPsd11ynkgL5Y+MlCg==", + "requires": { + "@redocly/ajv": "^8.11.0", + "colorette": "^1.2.0", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "lodash.isequal": "^4.5.0", + "minimatch": "^5.0.1", + "node-fetch": "^2.6.1", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "@tailwindcss/typography": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz", @@ -1212,6 +1752,19 @@ "postcss-selector-parser": "6.0.10" } }, + "@typescript/vfs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz", + "integrity": "sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==", + "requires": { + "debug": "^4.1.1" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" + }, "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -1237,12 +1790,15 @@ "dev": true, "peer": true }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "peer": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "binary-extensions": { "version": "2.2.0", @@ -1308,6 +1864,11 @@ } } }, + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" + }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -1328,6 +1889,14 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, "didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -1342,6 +1911,11 @@ "dev": true, "peer": true }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -1514,6 +2088,24 @@ "dev": true, "peer": true }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -1534,6 +2126,11 @@ "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", "dev": true }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -1574,6 +2171,11 @@ "brace-expansion": "^1.1.7" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -1593,6 +2195,14 @@ "dev": true, "peer": true }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1624,6 +2234,25 @@ "wrappy": "1" } }, + "openapi-typescript": { + "version": "7.0.0-next.5", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.0.0-next.5.tgz", + "integrity": "sha512-zqEDw/FZkT0ndOCd8EybkDVwEYgaOh+ryWm6OCON70DmY9YqUnNSIVyRFVjN8hesa0bxOs9QOMzXAasczNdHbQ==", + "requires": { + "@redocly/openapi-core": "^1.4.1", + "ansi-colors": "^4.1.3", + "supports-color": "^9.4.0", + "typescript": "^5.3.2", + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==" + } + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1666,6 +2295,11 @@ "dev": true, "peer": true }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==" + }, "postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -1751,6 +2385,11 @@ "dev": true, "peer": true }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1778,6 +2417,11 @@ "picomatch": "^2.2.1" } }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, "resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", @@ -1830,6 +2474,11 @@ "ts-interface-checker": "^0.1.9" } }, + "supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==" + }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -1911,6 +2560,11 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -1918,12 +2572,39 @@ "dev": true, "peer": true }, + "typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -1937,6 +2618,16 @@ "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", "dev": true, "peer": true + }, + "yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==" + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" } } } diff --git a/package.json b/package.json index 21a89ba684..19f2849beb 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "@tailwindcss/typography": "^0.5.9" }, "dependencies": { + "@bufbuild/buf": "^1.29.0", + "@bufbuild/protobuf": "^1.7.2", + "@bufbuild/protoc-gen-es": "^1.7.2", "openapi-typescript": "7.0.0-next.5" } } diff --git a/yarn.lock b/yarn.lock index 3fd25f7438..8275503de3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,215 +2,813 @@ # yarn lockfile v1 +"@alloc/quick-lru@^5.2.0": + "integrity" "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" + "resolved" "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" + "version" "5.2.0" + +"@bufbuild/buf-darwin-arm64@1.29.0": + "integrity" "sha512-5hKxsARoY2WpWq1n5ONFqqGuauHb4yILKXCy37KRYCKiRLWmIP5yI3gWvWHKoH7sUJWTQmBqdJoCvYQr6ahQnw==" + "resolved" "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.29.0.tgz" + "version" "1.29.0" + +"@bufbuild/buf@^1.29.0": + "integrity" "sha512-euksXeFtvlvAV5j94LqXb69qQcJvFfo8vN1d3cx+IzhOKoipykuQQTq7mOWVo2R0kdk6yIMBLBofOYOsh0Df8g==" + "resolved" "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.29.0.tgz" + "version" "1.29.0" + optionalDependencies: + "@bufbuild/buf-darwin-arm64" "1.29.0" + "@bufbuild/buf-darwin-x64" "1.29.0" + "@bufbuild/buf-linux-aarch64" "1.29.0" + "@bufbuild/buf-linux-x64" "1.29.0" + "@bufbuild/buf-win32-arm64" "1.29.0" + "@bufbuild/buf-win32-x64" "1.29.0" + +"@bufbuild/protobuf@^1.7.2", "@bufbuild/protobuf@1.7.2": + "integrity" "sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ==" + "resolved" "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.7.2.tgz" + "version" "1.7.2" + +"@bufbuild/protoc-gen-es@^1.7.2": + "integrity" "sha512-yiRk/T+YGmpSVvIkybCjPt+QyM/pLWMO+MAiz6auvCsiAgfXfc5nFFosD4yBYXID55M6eIkgBcity1AoJ6I30A==" + "resolved" "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.7.2.tgz" + "version" "1.7.2" + dependencies: + "@bufbuild/protobuf" "^1.7.2" + "@bufbuild/protoplugin" "1.7.2" + +"@bufbuild/protoplugin@1.7.2": + "integrity" "sha512-N3QtO8XWD4F4alMtASWtxBw6BWXp4aLz7rPBXH4KTULdjpUHnq46g15TsrG0/8szZw6pIklTO3lFe14dl6ZYdA==" + "resolved" "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.7.2.tgz" + "version" "1.7.2" + dependencies: + "@bufbuild/protobuf" "1.7.2" + "@typescript/vfs" "^1.4.0" + "typescript" "4.5.2" + +"@jridgewell/gen-mapping@^0.3.2": + "integrity" "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==" + "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz" + "version" "0.3.3" + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + "integrity" "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" + "resolved" "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz" + "version" "3.1.1" + +"@jridgewell/set-array@^1.0.1": + "integrity" "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + "resolved" "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + "version" "1.1.2" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + "integrity" "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" + "version" "1.4.15" + +"@jridgewell/trace-mapping@^0.3.9": + "integrity" "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==" + "resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz" + "version" "0.3.19" + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@nodelib/fs.scandir@2.1.5": + "integrity" "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + "version" "2.1.5" + dependencies: + "@nodelib/fs.stat" "2.0.5" + "run-parallel" "^1.1.9" + +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + "integrity" "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + "version" "2.0.5" + +"@nodelib/fs.walk@^1.2.3": + "integrity" "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + "version" "1.2.8" + dependencies: + "@nodelib/fs.scandir" "2.1.5" + "fastq" "^1.6.0" + "@redocly/ajv@^8.11.0": - version "8.11.0" - resolved "https://registry.yarnpkg.com/@redocly/ajv/-/ajv-8.11.0.tgz#2fad322888dc0113af026e08fceb3e71aae495ae" - integrity sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw== + "integrity" "sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw==" + "resolved" "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz" + "version" "8.11.0" dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" "@redocly/openapi-core@^1.4.1": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.5.0.tgz#aacdb9030a041a53e38c9b5c51b1caa25ab7957b" - integrity sha512-AnDLoDl1+a7mZO4+lx0KG8zH04BQx4ez6yh403PuNl9/0ygbicPPc9QG/y0/0OImChOA+knKLpJazNFjzhOAeg== + "integrity" "sha512-VjUz3wrqcDbO1HfEB0AUzh6Y7T1jNJR4Jmgfs0ipuoipLjU5bDsdfKJGSSz2u0WpfmqklPsd11ynkgL5Y+MlCg==" + "resolved" "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.8.2.tgz" + "version" "1.8.2" dependencies: "@redocly/ajv" "^8.11.0" - "@types/node" "^14.11.8" - colorette "^1.2.0" - js-levenshtein "^1.1.6" - js-yaml "^4.1.0" - lodash.isequal "^4.5.0" - minimatch "^5.0.1" - node-fetch "^2.6.1" - pluralize "^8.0.0" - yaml-ast-parser "0.0.43" + "colorette" "^1.2.0" + "js-levenshtein" "^1.1.6" + "js-yaml" "^4.1.0" + "lodash.isequal" "^4.5.0" + "minimatch" "^5.0.1" + "node-fetch" "^2.6.1" + "pluralize" "^8.0.0" + "yaml-ast-parser" "0.0.43" "@tailwindcss/typography@^0.5.9": - version "0.5.10" - resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.10.tgz#2abde4c6d5c797ab49cf47610830a301de4c1e0a" - integrity sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw== - dependencies: - lodash.castarray "^4.4.0" - lodash.isplainobject "^4.0.6" - lodash.merge "^4.6.2" - postcss-selector-parser "6.0.10" - -"@types/node@^14.11.8": - version "14.18.63" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" - integrity sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ== - -ansi-colors@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -colorette@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" - integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -js-levenshtein@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" - integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -lodash.castarray@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" - integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== - -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -node-fetch@^2.6.1: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - -openapi-typescript@7.0.0-next.5: - version "7.0.0-next.5" - resolved "https://registry.yarnpkg.com/openapi-typescript/-/openapi-typescript-7.0.0-next.5.tgz#8a45091ac43e48e4e4d8a055778b0bd537520bfe" - integrity sha512-zqEDw/FZkT0ndOCd8EybkDVwEYgaOh+ryWm6OCON70DmY9YqUnNSIVyRFVjN8hesa0bxOs9QOMzXAasczNdHbQ== + "integrity" "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==" + "resolved" "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz" + "version" "0.5.9" + dependencies: + "lodash.castarray" "^4.4.0" + "lodash.isplainobject" "^4.0.6" + "lodash.merge" "^4.6.2" + "postcss-selector-parser" "6.0.10" + +"@typescript/vfs@^1.4.0": + "integrity" "sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==" + "resolved" "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz" + "version" "1.5.0" + dependencies: + "debug" "^4.1.1" + +"ansi-colors@^4.1.3": + "integrity" "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" + "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz" + "version" "4.1.3" + +"any-promise@^1.0.0": + "integrity" "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + "resolved" "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" + "version" "1.3.0" + +"anymatch@~3.1.2": + "integrity" "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==" + "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + "version" "3.1.3" + dependencies: + "normalize-path" "^3.0.0" + "picomatch" "^2.0.4" + +"arg@^5.0.2": + "integrity" "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + "resolved" "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" + "version" "5.0.2" + +"argparse@^2.0.1": + "integrity" "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + "version" "2.0.1" + +"balanced-match@^1.0.0": + "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + "version" "1.0.2" + +"binary-extensions@^2.0.0": + "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + "version" "2.2.0" + +"brace-expansion@^1.1.7": + "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" + "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + "version" "1.1.11" + dependencies: + "balanced-match" "^1.0.0" + "concat-map" "0.0.1" + +"brace-expansion@^2.0.1": + "integrity" "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==" + "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "balanced-match" "^1.0.0" + +"braces@^3.0.2", "braces@~3.0.2": + "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" + "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + "version" "3.0.2" + dependencies: + "fill-range" "^7.0.1" + +"camelcase-css@^2.0.1": + "integrity" "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + "resolved" "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + "version" "2.0.1" + +"chokidar@^3.5.3": + "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" + "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + "version" "3.5.3" + dependencies: + "anymatch" "~3.1.2" + "braces" "~3.0.2" + "glob-parent" "~5.1.2" + "is-binary-path" "~2.1.0" + "is-glob" "~4.0.1" + "normalize-path" "~3.0.0" + "readdirp" "~3.6.0" + optionalDependencies: + "fsevents" "~2.3.2" + +"colorette@^1.2.0": + "integrity" "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" + "resolved" "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz" + "version" "1.4.0" + +"commander@^4.0.0": + "integrity" "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + "resolved" "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" + "version" "4.1.1" + +"concat-map@0.0.1": + "integrity" "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "version" "0.0.1" + +"cssesc@^3.0.0": + "integrity" "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + "resolved" "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + "version" "3.0.0" + +"debug@^4.1.1": + "integrity" "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + "version" "4.3.4" + dependencies: + "ms" "2.1.2" + +"didyoumean@^1.2.2": + "integrity" "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + "resolved" "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" + "version" "1.2.2" + +"dlv@^1.1.3": + "integrity" "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + "resolved" "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" + "version" "1.1.3" + +"fast-deep-equal@^3.1.1": + "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + "version" "3.1.3" + +"fast-glob@^3.2.12": + "integrity" "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==" + "resolved" "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz" + "version" "3.3.1" + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + "glob-parent" "^5.1.2" + "merge2" "^1.3.0" + "micromatch" "^4.0.4" + +"fastq@^1.6.0": + "integrity" "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==" + "resolved" "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" + "version" "1.15.0" + dependencies: + "reusify" "^1.0.4" + +"fill-range@^7.0.1": + "integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" + "resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + "version" "7.0.1" + dependencies: + "to-regex-range" "^5.0.1" + +"fs.realpath@^1.0.0": + "integrity" "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "version" "1.0.0" + +"fsevents@~2.3.2": + "integrity" "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==" + "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" + "version" "2.3.3" + +"function-bind@^1.1.1": + "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + "version" "1.1.1" + +"glob-parent@^5.1.2": + "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "is-glob" "^4.0.1" + +"glob-parent@^6.0.2": + "integrity" "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + "version" "6.0.2" + dependencies: + "is-glob" "^4.0.3" + +"glob-parent@~5.1.2": + "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "is-glob" "^4.0.1" + +"glob@7.1.6": + "integrity" "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==" + "resolved" "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" + "version" "7.1.6" + dependencies: + "fs.realpath" "^1.0.0" + "inflight" "^1.0.4" + "inherits" "2" + "minimatch" "^3.0.4" + "once" "^1.3.0" + "path-is-absolute" "^1.0.0" + +"has@^1.0.3": + "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==" + "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + "version" "1.0.3" + dependencies: + "function-bind" "^1.1.1" + +"inflight@^1.0.4": + "integrity" "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==" + "resolved" "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + "version" "1.0.6" + dependencies: + "once" "^1.3.0" + "wrappy" "1" + +"inherits@2": + "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + "version" "2.0.4" + +"is-binary-path@~2.1.0": + "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" + "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "binary-extensions" "^2.0.0" + +"is-core-module@^2.13.0": + "integrity" "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==" + "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz" + "version" "2.13.0" + dependencies: + "has" "^1.0.3" + +"is-extglob@^2.1.1": + "integrity" "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + "version" "2.1.1" + +"is-glob@^4.0.1", "is-glob@^4.0.3", "is-glob@~4.0.1": + "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==" + "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + "version" "4.0.3" + dependencies: + "is-extglob" "^2.1.1" + +"is-number@^7.0.0": + "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + "version" "7.0.0" + +"jiti@^1.18.2": + "integrity" "sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w==" + "resolved" "https://registry.npmjs.org/jiti/-/jiti-1.19.3.tgz" + "version" "1.19.3" + +"js-levenshtein@^1.1.6": + "integrity" "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==" + "resolved" "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz" + "version" "1.1.6" + +"js-yaml@^4.1.0": + "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "argparse" "^2.0.1" + +"json-schema-traverse@^1.0.0": + "integrity" "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + "version" "1.0.0" + +"lilconfig@^2.0.5", "lilconfig@^2.1.0": + "integrity" "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" + "resolved" "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" + "version" "2.1.0" + +"lines-and-columns@^1.1.6": + "integrity" "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "resolved" "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + "version" "1.2.4" + +"lodash.castarray@^4.4.0": + "integrity" "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==" + "resolved" "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz" + "version" "4.4.0" + +"lodash.isequal@^4.5.0": + "integrity" "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + "resolved" "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz" + "version" "4.5.0" + +"lodash.isplainobject@^4.0.6": + "integrity" "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + "resolved" "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + "version" "4.0.6" + +"lodash.merge@^4.6.2": + "integrity" "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "resolved" "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + "version" "4.6.2" + +"merge2@^1.3.0": + "integrity" "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + "resolved" "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + "version" "1.4.1" + +"micromatch@^4.0.4", "micromatch@^4.0.5": + "integrity" "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==" + "resolved" "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + "version" "4.0.5" + dependencies: + "braces" "^3.0.2" + "picomatch" "^2.3.1" + +"minimatch@^3.0.4": + "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + "version" "3.1.2" + dependencies: + "brace-expansion" "^1.1.7" + +"minimatch@^5.0.1": + "integrity" "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" + "version" "5.1.6" + dependencies: + "brace-expansion" "^2.0.1" + +"ms@2.1.2": + "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + "version" "2.1.2" + +"mz@^2.7.0": + "integrity" "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==" + "resolved" "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" + "version" "2.7.0" + dependencies: + "any-promise" "^1.0.0" + "object-assign" "^4.0.1" + "thenify-all" "^1.0.0" + +"nanoid@^3.3.6": + "integrity" "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz" + "version" "3.3.6" + +"node-fetch@^2.6.1": + "integrity" "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==" + "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" + "version" "2.7.0" + dependencies: + "whatwg-url" "^5.0.0" + +"normalize-path@^3.0.0", "normalize-path@~3.0.0": + "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + "version" "3.0.0" + +"object-assign@^4.0.1": + "integrity" "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "resolved" "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "version" "4.1.1" + +"object-hash@^3.0.0": + "integrity" "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + "resolved" "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + "version" "3.0.0" + +"once@^1.3.0": + "integrity" "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==" + "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "version" "1.4.0" + dependencies: + "wrappy" "1" + +"openapi-typescript@7.0.0-next.5": + "integrity" "sha512-zqEDw/FZkT0ndOCd8EybkDVwEYgaOh+ryWm6OCON70DmY9YqUnNSIVyRFVjN8hesa0bxOs9QOMzXAasczNdHbQ==" + "resolved" "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.0.0-next.5.tgz" + "version" "7.0.0-next.5" dependencies: "@redocly/openapi-core" "^1.4.1" - ansi-colors "^4.1.3" - supports-color "^9.4.0" - typescript "^5.3.2" - yargs-parser "^21.1.1" - -pluralize@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" - integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== - -postcss-selector-parser@6.0.10: - version "6.0.10" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" - integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - -punycode@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -supports-color@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" - integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -typescript@^5.3.2: - version "5.3.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" - integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -util-deprecate@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -yaml-ast-parser@0.0.43: - version "0.0.43" - resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb" - integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== - -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + "ansi-colors" "^4.1.3" + "supports-color" "^9.4.0" + "typescript" "^5.3.2" + "yargs-parser" "^21.1.1" + +"path-is-absolute@^1.0.0": + "integrity" "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "version" "1.0.1" + +"path-parse@^1.0.7": + "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + "version" "1.0.7" + +"picocolors@^1.0.0": + "integrity" "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + "version" "1.0.0" + +"picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.3.1": + "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + "version" "2.3.1" + +"pify@^2.3.0": + "integrity" "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + "resolved" "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + "version" "2.3.0" + +"pirates@^4.0.1": + "integrity" "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==" + "resolved" "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" + "version" "4.0.6" + +"pluralize@^8.0.0": + "integrity" "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==" + "resolved" "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz" + "version" "8.0.0" + +"postcss-import@^15.1.0": + "integrity" "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==" + "resolved" "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz" + "version" "15.1.0" + dependencies: + "postcss-value-parser" "^4.0.0" + "read-cache" "^1.0.0" + "resolve" "^1.1.7" + +"postcss-js@^4.0.1": + "integrity" "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==" + "resolved" "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "camelcase-css" "^2.0.1" + +"postcss-load-config@^4.0.1": + "integrity" "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==" + "resolved" "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "lilconfig" "^2.0.5" + "yaml" "^2.1.1" + +"postcss-nested@^6.0.1": + "integrity" "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==" + "resolved" "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz" + "version" "6.0.1" + dependencies: + "postcss-selector-parser" "^6.0.11" + +"postcss-selector-parser@^6.0.11": + "integrity" "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==" + "resolved" "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz" + "version" "6.0.13" + dependencies: + "cssesc" "^3.0.0" + "util-deprecate" "^1.0.2" + +"postcss-selector-parser@6.0.10": + "integrity" "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==" + "resolved" "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz" + "version" "6.0.10" + dependencies: + "cssesc" "^3.0.0" + "util-deprecate" "^1.0.2" + +"postcss-value-parser@^4.0.0": + "integrity" "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "resolved" "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + "version" "4.2.0" + +"postcss@^8.0.0", "postcss@^8.2.14", "postcss@^8.4.21", "postcss@^8.4.23", "postcss@>=8.0.9": + "integrity" "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==" + "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" + "version" "8.4.31" + dependencies: + "nanoid" "^3.3.6" + "picocolors" "^1.0.0" + "source-map-js" "^1.0.2" + +"punycode@^2.1.0": + "integrity" "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + "resolved" "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + "version" "2.3.1" + +"queue-microtask@^1.2.2": + "integrity" "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + "resolved" "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + "version" "1.2.3" + +"read-cache@^1.0.0": + "integrity" "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==" + "resolved" "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "pify" "^2.3.0" + +"readdirp@~3.6.0": + "integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==" + "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + "version" "3.6.0" + dependencies: + "picomatch" "^2.2.1" + +"require-from-string@^2.0.2": + "integrity" "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + "resolved" "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + "version" "2.0.2" + +"resolve@^1.1.7", "resolve@^1.22.2": + "integrity" "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==" + "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz" + "version" "1.22.4" + dependencies: + "is-core-module" "^2.13.0" + "path-parse" "^1.0.7" + "supports-preserve-symlinks-flag" "^1.0.0" + +"reusify@^1.0.4": + "integrity" "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + "resolved" "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + "version" "1.0.4" + +"run-parallel@^1.1.9": + "integrity" "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==" + "resolved" "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + "version" "1.2.0" + dependencies: + "queue-microtask" "^1.2.2" + +"source-map-js@^1.0.2": + "integrity" "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + "version" "1.0.2" + +"sucrase@^3.32.0": + "integrity" "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==" + "resolved" "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz" + "version" "3.34.0" + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + "commander" "^4.0.0" + "glob" "7.1.6" + "lines-and-columns" "^1.1.6" + "mz" "^2.7.0" + "pirates" "^4.0.1" + "ts-interface-checker" "^0.1.9" + +"supports-color@^9.4.0": + "integrity" "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz" + "version" "9.4.0" + +"supports-preserve-symlinks-flag@^1.0.0": + "integrity" "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "resolved" "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + "version" "1.0.0" + +"tailwindcss@>=3.0.0 || insiders": + "integrity" "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==" + "resolved" "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz" + "version" "3.3.3" + dependencies: + "@alloc/quick-lru" "^5.2.0" + "arg" "^5.0.2" + "chokidar" "^3.5.3" + "didyoumean" "^1.2.2" + "dlv" "^1.1.3" + "fast-glob" "^3.2.12" + "glob-parent" "^6.0.2" + "is-glob" "^4.0.3" + "jiti" "^1.18.2" + "lilconfig" "^2.1.0" + "micromatch" "^4.0.5" + "normalize-path" "^3.0.0" + "object-hash" "^3.0.0" + "picocolors" "^1.0.0" + "postcss" "^8.4.23" + "postcss-import" "^15.1.0" + "postcss-js" "^4.0.1" + "postcss-load-config" "^4.0.1" + "postcss-nested" "^6.0.1" + "postcss-selector-parser" "^6.0.11" + "resolve" "^1.22.2" + "sucrase" "^3.32.0" + +"thenify-all@^1.0.0": + "integrity" "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==" + "resolved" "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" + "version" "1.6.0" + dependencies: + "thenify" ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + "integrity" "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==" + "resolved" "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" + "version" "3.3.1" + dependencies: + "any-promise" "^1.0.0" + +"to-regex-range@^5.0.1": + "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" + "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + "version" "5.0.1" + dependencies: + "is-number" "^7.0.0" + +"tr46@~0.0.3": + "integrity" "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + "version" "0.0.3" + +"ts-interface-checker@^0.1.9": + "integrity" "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + "resolved" "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" + "version" "0.1.13" + +"typescript@^5.3.2": + "integrity" "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz" + "version" "5.3.3" + +"typescript@4.5.2": + "integrity" "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz" + "version" "4.5.2" + +"uri-js@^4.2.2": + "integrity" "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==" + "resolved" "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + "version" "4.4.1" + dependencies: + "punycode" "^2.1.0" + +"util-deprecate@^1.0.2": + "integrity" "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "version" "1.0.2" + +"webidl-conversions@^3.0.0": + "integrity" "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + "version" "3.0.1" + +"whatwg-url@^5.0.0": + "integrity" "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==" + "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "tr46" "~0.0.3" + "webidl-conversions" "^3.0.0" + +"wrappy@1": + "integrity" "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "version" "1.0.2" + +"yaml-ast-parser@0.0.43": + "integrity" "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==" + "resolved" "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz" + "version" "0.0.43" + +"yaml@^2.1.1": + "integrity" "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==" + "resolved" "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz" + "version" "2.3.2" + +"yargs-parser@^21.1.1": + "integrity" "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + "version" "21.1.1" From 390ead6f04db3b9a02ee60624c9b80f8de39e697 Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Thu, 15 Feb 2024 10:22:49 -0500 Subject: [PATCH 067/102] chore(main): release 0.86.24 (#2166) :robot: I have created a release *beep* *boop* --- ## [0.86.24](https://github.com/kurtosis-tech/kurtosis/compare/0.86.23...0.86.24) (2024-02-15) ### Features * added a `description` field to instructions ([#2147](https://github.com/kurtosis-tech/kurtosis/issues/2147)) ([9085cfd](https://github.com/kurtosis-tech/kurtosis/commit/9085cfd7d1ad51b65e2087e124cc24cb487364b8)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 7 +++++++ LICENSE.md | 2 +- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- api/typescript/src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- enclave-manager/web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdb466d21d..da20b85f25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.86.24](https://github.com/kurtosis-tech/kurtosis/compare/0.86.23...0.86.24) (2024-02-15) + + +### Features + +* added a `description` field to instructions ([#2147](https://github.com/kurtosis-tech/kurtosis/issues/2147)) ([9085cfd](https://github.com/kurtosis-tech/kurtosis/commit/9085cfd7d1ad51b65e2087e124cc24cb487364b8)) + ## [0.86.23](https://github.com/kurtosis-tech/kurtosis/compare/0.86.22...0.86.23) (2024-02-15) diff --git a/LICENSE.md b/LICENSE.md index da1d5d699c..415beffcb4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.23 +Licensed Work: Kurtosis 0.86.24 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 007fc83885..9fc7a08365 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.23" + KurtosisVersion = "0.86.24" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 7a551ed10a..2affd81e7f 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.23" +version = "0.86.24" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index 8b64e6d9e1..cd1e66154e 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.23", + "version": "0.86.24", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index 18f1e20174..bb707b6607 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.23" +export const KURTOSIS_VERSION: string = "0.86.24" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 8e5907d739..22c5942cec 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.23", + "version": "0.86.24", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index c0ff51cb1d..0d056c142e 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.23", + "version": "0.86.24", "private": true, "homepage": ".", "dependencies": { @@ -10,7 +10,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.23", + "kurtosis-ui-components": "0.86.24", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "react-mentions": "^4.4.10", diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index be232d3760..52d18682a6 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.23", + "version": "0.86.24", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 23b9e21456..df15585f8d 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.23 +0.86.24 From 6c70247531eb0343434101fe54cf6ef028ded873 Mon Sep 17 00:00:00 2001 From: Tedi Mitiku Date: Thu, 15 Feb 2024 11:10:59 -0500 Subject: [PATCH 068/102] feat: kurtosis run `--non-blocking-tasks` (#2153) ## Description: Adds a flag `--non-blocking-tasks`. If set, task services from `run_sh` and `run_python` are not removed before continuing with execution. The effects is small over Docker, but this significantly reduces run times over k8s because of the overhead of removing a pod (up to 30s). For the `ethereum-package` over k8s with images cached, running with the flag goes from 4min to 1min. However, task services will have to be removed manually after execution or via a `kurtosis clean`. ## Is this change user facing? YES ## References: https://github.com/kurtosis-tech/kurtosis/issues/2140 --- .../api_container_service.pb.go | 1129 +++++++++-------- .../api_container_service_grpc.pb.go | 2 +- .../binding_constructors.go | 6 + .../core/lib/enclaves/enclave_context.go | 31 +- .../starlark_run_config.go | 9 + .../engine_service.pb.go | 4 +- .../engine_service_grpc.pb.go | 2 +- .../http_rest/api_types/api_types.gen.go | 6 + .../core_rest_api/api_container_server.gen.go | 154 +-- api/openapi/specs/kurtosis_api.yaml | 6 + api/protobuf/core/api_container_service.proto | 6 + api/rust/src/api_container_api.rs | 6 + .../api_container_service_grpc_web_pb.d.ts | 4 +- .../api_container_service_grpc_web_pb.js | 2 +- .../api_container_service_pb.d.ts | 24 +- .../api_container_service_pb.js | 100 +- .../api_container_service_connect.d.ts | 2 +- .../connect/api_container_service_connect.js | 2 +- .../connect/api_container_service_pb.d.ts | 16 +- .../connect/api_container_service_pb.js | 4 +- .../connect/engine_service_connect.d.ts | 2 +- .../connect/engine_service_connect.js | 2 +- .../connect/engine_service_pb.d.ts | 2 +- .../connect/engine_service_pb.js | 2 +- .../engine_service_grpc_web_pb.d.ts | 4 +- .../engine_service_grpc_web_pb.js | 2 +- .../engine_service_pb.d.ts | 4 +- .../src/engine/rest_api_bindings/types.d.ts | 4 + cli/cli/commands/run/run.go | 15 + .../server/api_container_service.go | 22 +- .../startosis_engine/kurtosis_builtins.go | 5 +- .../kurtosis_instruction/tasks/run_python.go | 18 +- .../kurtosis_instruction/tasks/run_sh.go | 18 +- .../tasks/tasks_shared.go | 2 +- .../startosis_engine/startosis_interpreter.go | 8 +- .../startosis_interpreter_idempotent_test.go | 14 + .../startosis_interpreter_test.go | 80 +- .../startosis_engine/startosis_runner.go | 4 +- .../engine/server/enclave_rest_api_handler.go | 2 + package-lock.json | 95 ++ yarn.lock | 786 ++++++++++++ 41 files changed, 1888 insertions(+), 718 deletions(-) diff --git a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go index 6c9a6ffbdf..6407ae89b5 100644 --- a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go +++ b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc-gen-go v1.32.0 +// protoc v4.25.2 // source: api_container_service.proto package kurtosis_core_rpc_api_bindings @@ -655,6 +655,8 @@ type RunStarlarkScriptArgs struct { CloudUserId *string `protobuf:"bytes,8,opt,name=cloud_user_id,json=cloudUserId,proto3,oneof" json:"cloud_user_id,omitempty"` // Defaults to empty ImageDownloadMode *ImageDownloadMode `protobuf:"varint,9,opt,name=image_download_mode,json=imageDownloadMode,proto3,enum=api_container_api.ImageDownloadMode,oneof" json:"image_download_mode,omitempty"` + // Defaults to false + NonBlockingMode *bool `protobuf:"varint,10,opt,name=non_blocking_mode,json=nonBlockingMode,proto3,oneof" json:"non_blocking_mode,omitempty"` } func (x *RunStarlarkScriptArgs) Reset() { @@ -752,6 +754,13 @@ func (x *RunStarlarkScriptArgs) GetImageDownloadMode() ImageDownloadMode { return ImageDownloadMode_always } +func (x *RunStarlarkScriptArgs) GetNonBlockingMode() bool { + if x != nil && x.NonBlockingMode != nil { + return *x.NonBlockingMode + } + return false +} + type RunStarlarkPackageArgs struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -792,6 +801,8 @@ type RunStarlarkPackageArgs struct { CloudUserId *string `protobuf:"bytes,13,opt,name=cloud_user_id,json=cloudUserId,proto3,oneof" json:"cloud_user_id,omitempty"` // Defaults to empty ImageDownloadMode *ImageDownloadMode `protobuf:"varint,14,opt,name=image_download_mode,json=imageDownloadMode,proto3,enum=api_container_api.ImageDownloadMode,oneof" json:"image_download_mode,omitempty"` + // Defaults to false + NonBlockingMode *bool `protobuf:"varint,15,opt,name=non_blocking_mode,json=nonBlockingMode,proto3,oneof" json:"non_blocking_mode,omitempty"` } func (x *RunStarlarkPackageArgs) Reset() { @@ -924,6 +935,13 @@ func (x *RunStarlarkPackageArgs) GetImageDownloadMode() ImageDownloadMode { return ImageDownloadMode_always } +func (x *RunStarlarkPackageArgs) GetNonBlockingMode() bool { + if x != nil && x.NonBlockingMode != nil { + return *x.NonBlockingMode + } + return false +} + type isRunStarlarkPackageArgs_StarlarkPackageContent interface { isRunStarlarkPackageArgs_StarlarkPackageContent() } @@ -3362,7 +3380,7 @@ var file_api_container_service_proto_rawDesc = []byte{ 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x89, 0x05, 0x0a, 0x15, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd0, 0x05, 0x0a, 0x15, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x69, @@ -3394,569 +3412,578 @@ var file_api_container_service_proto_rawDesc = []byte{ 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x48, 0x06, 0x52, 0x11, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, - 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x65, 0x72, + 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x6e, 0x6f, 0x6e, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x08, 0x48, 0x07, 0x52, 0x0f, 0x6e, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, + 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, + 0x0a, 0x0a, 0x08, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x42, 0x15, 0x0a, 0x13, 0x5f, + 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6d, 0x6f, + 0x64, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0xad, 0x07, 0x0a, 0x16, 0x52, 0x75, 0x6e, + 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x41, + 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, + 0x49, 0x64, 0x12, 0x16, 0x0a, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x48, 0x00, 0x52, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x06, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x30, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x01, 0x52, 0x10, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, + 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, + 0x6e, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, + 0x69, 0x73, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x48, 0x03, 0x52, 0x0b, 0x70, 0x61, 0x72, + 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x0d, 0x63, + 0x6c, 0x6f, 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x08, 0x48, 0x04, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, + 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3f, 0x0a, 0x1a, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, + 0x69, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x16, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x54, 0x6f, 0x4d, 0x61, 0x69, 0x6e, 0x46, + 0x69, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x06, 0x52, 0x10, 0x6d, 0x61, 0x69, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x5b, 0x0a, 0x15, 0x65, 0x78, 0x70, + 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4b, 0x75, 0x72, + 0x74, 0x6f, 0x73, 0x69, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, + 0x52, 0x14, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x11, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x07, 0x52, 0x0f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x08, + 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x88, 0x01, 0x01, + 0x12, 0x59, 0x0a, 0x13, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, + 0x61, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, + 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, + 0x69, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4d, + 0x6f, 0x64, 0x65, 0x48, 0x09, 0x52, 0x11, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x77, 0x6e, + 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x6e, + 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x48, 0x0a, 0x52, 0x0f, 0x6e, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x42, 0x1a, 0x0a, 0x18, + 0x73, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, - 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6d, - 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6c, 0x6f, 0x75, - 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x69, 0x6d, - 0x61, 0x67, 0x65, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x22, 0xe6, 0x06, 0x0a, 0x16, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, - 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x0a, - 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x05, 0x6c, - 0x6f, 0x63, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x30, 0x0a, - 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x10, 0x73, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x88, 0x01, 0x01, 0x12, - 0x1c, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x48, 0x02, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, - 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x05, 0x48, 0x03, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, - 0x6d, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x5f, 0x70, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x04, 0x52, 0x0c, 0x63, - 0x6c, 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3f, - 0x0a, 0x1a, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, - 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x05, 0x52, 0x16, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, - 0x74, 0x68, 0x54, 0x6f, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x12, - 0x31, 0x0a, 0x12, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x06, 0x52, 0x10, 0x6d, - 0x61, 0x69, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x88, - 0x01, 0x01, 0x12, 0x5b, 0x0a, 0x15, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, - 0x61, 0x6c, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, - 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x46, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x14, 0x65, 0x78, 0x70, 0x65, 0x72, - 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, - 0x2f, 0x0a, 0x11, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x07, 0x52, 0x0f, 0x63, 0x6c, - 0x6f, 0x75, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, - 0x12, 0x27, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x08, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x75, 0x64, - 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x59, 0x0a, 0x13, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x48, 0x09, 0x52, 0x11, - 0x69, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x6f, 0x64, - 0x65, 0x88, 0x01, 0x01, 0x42, 0x1a, 0x0a, 0x18, 0x73, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, - 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, - 0x75, 0x6e, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, - 0x73, 0x6d, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x63, - 0x6b, 0x61, 0x67, 0x65, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, - 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, - 0x69, 0x6c, 0x65, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x63, - 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x6f, 0x77, - 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0xb6, 0x04, 0x0a, 0x17, 0x53, - 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x70, - 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4d, 0x0a, 0x0d, - 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, - 0x52, 0x75, 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x70, - 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5d, 0x0a, 0x12, 0x69, - 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, - 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x11, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x5b, 0x0a, 0x12, 0x72, 0x75, - 0x6e, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, - 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x10, 0x72, 0x75, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, - 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, - 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, - 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x07, - 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x35, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, + 0x6c, 0x6f, 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x42, 0x1d, 0x0a, 0x1b, + 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, + 0x6f, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x15, 0x0a, 0x13, 0x5f, + 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6d, 0x6f, + 0x64, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0xb6, 0x04, 0x0a, 0x17, 0x53, 0x74, 0x61, + 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, + 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x48, 0x00, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x38, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4d, 0x0a, 0x0d, 0x70, 0x72, + 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, + 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x70, 0x72, 0x6f, + 0x67, 0x72, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5d, 0x0a, 0x12, 0x69, 0x6e, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, - 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x42, 0x13, - 0x0a, 0x11, 0x72, 0x75, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6c, - 0x69, 0x6e, 0x65, 0x22, 0x31, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x66, 0x6f, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x3a, 0x0a, 0x0f, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, - 0x72, 0x6b, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x61, 0x72, - 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x22, 0xcd, 0x02, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, - 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x08, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x61, - 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, - 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x47, 0x0a, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x11, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x5b, 0x0a, 0x12, 0x72, 0x75, 0x6e, 0x5f, + 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, - 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x52, - 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x35, 0x0a, 0x16, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x5f, 0x0a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, - 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, - 0x42, 0x0a, 0x1d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x69, 0x6e, - 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x22, 0xa4, 0x01, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, - 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x12, 0x30, - 0x0a, 0x14, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x67, - 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x41, 0x72, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x12, 0x1e, 0x0a, 0x08, 0x61, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x61, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, - 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, - 0x61, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x52, - 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x0b, 0x0a, - 0x09, 0x5f, 0x61, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x1b, 0x53, 0x74, - 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6c, - 0x75, 0x6d, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x22, 0xac, 0x02, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x12, 0x63, 0x0a, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x48, 0x00, 0x52, 0x13, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x57, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, - 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x12, 0x54, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, - 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x22, 0x42, 0x0a, 0x1b, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x3e, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x3d, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, - 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, - 0x52, 0x75, 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, - 0x74, 0x65, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x5f, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x53, 0x74, 0x65, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, - 0x65, 0x70, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x53, 0x74, 0x61, - 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x72, 0x75, 0x6e, 0x5f, - 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0f, 0x69, 0x73, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, - 0x6c, 0x12, 0x30, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, - 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, - 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xc5, 0x01, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x6b, 0x0a, - 0x13, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x70, 0x69, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xd1, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0c, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x37, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, - 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, - 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x5e, 0x0a, 0x10, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, + 0x6b, 0x52, 0x75, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x48, 0x00, 0x52, 0x10, 0x72, 0x75, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, + 0x61, 0x72, 0x6b, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x07, 0x77, 0x61, + 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x35, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, + 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x42, 0x13, 0x0a, 0x11, + 0x72, 0x75, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6c, 0x69, 0x6e, + 0x65, 0x22, 0x31, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x66, 0x6f, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0x3a, 0x0a, 0x0f, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, + 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0xcd, 0x02, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x72, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x5f, - 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x65, 0x6e, 0x65, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x32, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4d, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x47, 0x0a, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, + 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x52, 0x09, 0x61, + 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x35, 0x0a, 0x16, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x5f, 0x0a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x42, 0x0a, + 0x1d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x22, 0xa4, 0x01, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x12, 0x30, 0x0a, 0x14, + 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x67, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x41, 0x72, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1e, + 0x0a, 0x08, 0x61, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x07, 0x61, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2b, + 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x52, 0x65, 0x70, + 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, + 0x61, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x1b, 0x53, 0x74, 0x61, 0x72, + 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x22, + 0xac, 0x02, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x12, 0x63, 0x0a, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, + 0x00, 0x52, 0x13, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x57, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0f, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, + 0x54, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, + 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x42, + 0x0a, 0x1b, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x70, + 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, + 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x3e, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, + 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x3d, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x45, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x92, 0x01, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, + 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x65, + 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, + 0x74, 0x65, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x53, 0x74, 0x65, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x65, 0x70, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x6c, + 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x73, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, + 0x69, 0x73, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x12, + 0x30, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x88, 0x01, + 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xc5, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x6b, 0x0a, 0x13, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x2e, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0xd1, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, + 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, + 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x1a, 0x5e, 0x0a, 0x10, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, - 0x0e, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x22, - 0x63, 0x0a, 0x0f, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, - 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x72, 0x67, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x41, 0x72, 0x67, 0x73, 0x22, 0x51, 0x0a, 0x13, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x65, - 0x78, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, - 0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, - 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, - 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xac, 0x03, 0x0a, 0x26, 0x57, 0x61, 0x69, 0x74, - 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, - 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x41, - 0x0a, 0x1a, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, - 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0d, 0x48, 0x01, 0x52, 0x18, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, 0x6c, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x72, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, 0x6e, 0x65, 0x64, 0x5f, 0x75, 0x75, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x65, + 0x6e, 0x65, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x32, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, + 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0e, 0x61, + 0x6c, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x22, 0x63, 0x0a, + 0x0f, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, + 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, + 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, + 0x67, 0x73, 0x22, 0x51, 0x0a, 0x13, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x69, + 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, + 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x6f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x4f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xac, 0x03, 0x0a, 0x26, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, + 0x72, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, 0x67, 0x73, + 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, + 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, + 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x48, 0x01, 0x52, 0x18, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, + 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, + 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, + 0x48, 0x02, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x41, + 0x0a, 0x1a, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, + 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0d, 0x48, 0x03, 0x52, 0x18, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, - 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0d, 0x48, 0x02, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x88, 0x01, 0x01, - 0x12, 0x41, 0x0a, 0x1a, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, - 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x18, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x44, - 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, - 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x08, 0x62, 0x6f, 0x64, 0x79, 0x54, 0x65, - 0x78, 0x74, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x1d, - 0x0a, 0x1b, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, - 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0a, 0x0a, - 0x08, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x65, - 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, - 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x62, 0x6f, 0x64, - 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x22, 0xe6, 0x03, 0x0a, 0x27, 0x57, 0x61, 0x69, 0x74, 0x46, - 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, - 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x26, - 0x0a, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, - 0x6f, 0x64, 0x79, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, - 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x18, 0x69, 0x6e, - 0x69, 0x74, 0x69, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x74, - 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x07, 0x72, 0x65, - 0x74, 0x72, 0x69, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, 0x72, 0x65, 0x74, 0x72, + 0x01, 0x12, 0x20, 0x0a, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x08, 0x62, 0x6f, 0x64, 0x79, 0x54, 0x65, 0x78, 0x74, + 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x1d, 0x0a, 0x1b, + 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, + 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, + 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x04, 0x52, 0x18, - 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, - 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x62, - 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, - 0x52, 0x08, 0x62, 0x6f, 0x64, 0x79, 0x54, 0x65, 0x78, 0x74, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, - 0x05, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x69, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, - 0x65, 0x73, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, - 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x22, - 0x99, 0x01, 0x0a, 0x11, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x65, - 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x40, 0x0a, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, - 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x27, 0x0a, 0x11, 0x44, - 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x45, 0x0a, 0x1b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x19, 0x44, - 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, - 0x66, 0x61, 0x63, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x19, 0x53, 0x74, 0x6f, 0x72, - 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, - 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x33, 0x0a, 0x1d, 0x53, - 0x74, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, - 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, - 0x22, 0x87, 0x01, 0x0a, 0x21, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, - 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x25, 0x53, 0x74, - 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, - 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x52, 0x0a, 0x18, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, - 0x75, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x26, - 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x14, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, + 0x74, 0x65, 0x78, 0x74, 0x22, 0xe6, 0x03, 0x0a, 0x27, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, + 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, 0x67, 0x73, + 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, + 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, + 0x79, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x18, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72, + 0x69, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1a, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, + 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x04, 0x52, 0x18, 0x72, 0x65, + 0x74, 0x72, 0x69, 0x65, 0x73, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x62, 0x6f, 0x64, + 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x08, + 0x62, 0x6f, 0x64, 0x79, 0x54, 0x65, 0x78, 0x74, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, + 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, + 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, + 0x0c, 0x0a, 0x0a, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x22, 0x99, 0x01, + 0x0a, 0x11, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, + 0x75, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x6f, 0x75, 0x73, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x43, 0x68, + 0x75, 0x6e, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x40, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x27, 0x0a, 0x11, 0x44, 0x61, 0x74, + 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x45, 0x0a, 0x1b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, + 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x19, 0x44, 0x6f, 0x77, + 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, + 0x63, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x19, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, + 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x41, + 0x72, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x33, 0x0a, 0x1d, 0x53, 0x74, 0x6f, + 0x72, 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, + 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x87, + 0x01, 0x0a, 0x21, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, + 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x41, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x25, 0x53, 0x74, 0x6f, 0x72, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, + 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x52, 0x0a, 0x18, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, - 0x64, 0x52, 0x11, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, - 0x75, 0x69, 0x64, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x23, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x13, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, - 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, - 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x8b, 0x01, 0x0a, 0x24, 0x49, 0x6e, 0x73, - 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, - 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x63, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, - 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, - 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x23, 0x46, 0x69, 0x6c, 0x65, 0x41, - 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, - 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, - 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x26, 0x0a, 0x0c, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, - 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, - 0x74, 0x65, 0x78, 0x74, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x88, 0x01, 0x01, 0x42, 0x0f, - 0x0a, 0x0d, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x22, - 0x4b, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x22, 0x19, 0x0a, 0x17, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x03, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, - 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x49, - 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x2b, - 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x69, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x70, - 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x12, 0x3a, 0x0a, - 0x1a, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, - 0x6f, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x16, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x54, - 0x6f, 0x4d, 0x61, 0x69, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x69, - 0x6e, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x61, 0x69, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x5b, 0x0a, 0x15, 0x65, 0x78, 0x70, 0x65, 0x72, - 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4b, 0x75, 0x72, 0x74, 0x6f, - 0x73, 0x69, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x14, - 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x61, + 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x26, 0x4c, 0x69, + 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x14, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x52, + 0x11, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, + 0x64, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x23, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x13, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x75, 0x75, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, 0x69, 0x6c, 0x65, + 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x41, 0x6e, 0x64, + 0x55, 0x75, 0x69, 0x64, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, + 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x22, 0x8b, 0x01, 0x0a, 0x24, 0x49, 0x6e, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x63, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x70, 0x69, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x46, + 0x69, 0x6c, 0x65, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x23, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x72, 0x74, + 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x69, 0x6c, + 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x12, 0x26, 0x0a, 0x0c, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, + 0x78, 0x74, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, + 0x5f, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x22, 0x4b, 0x0a, + 0x13, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x41, 0x72, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x03, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, + 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, + 0x2b, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x2b, 0x0a, 0x11, + 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, + 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, + 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d, 0x12, 0x3a, 0x0a, 0x1a, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x5f, + 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x16, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x54, 0x6f, 0x4d, + 0x61, 0x69, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x69, 0x6e, 0x5f, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x61, 0x69, 0x6e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x5b, 0x0a, 0x15, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, + 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x07, + 0x20, 0x03, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, + 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x14, 0x65, 0x78, + 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x52, + 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, 0x72, 0x65, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2a, 0x36, 0x0a, 0x0d, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, + 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x55, 0x4e, + 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x02, 0x2a, 0x2c, 0x0a, 0x11, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x77, 0x6e, + 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x10, + 0x01, 0x2a, 0x26, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x0b, 0x0a, 0x07, + 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x4f, 0x5f, + 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x01, 0x2a, 0x32, 0x0a, 0x13, 0x4b, 0x75, 0x72, + 0x74, 0x6f, 0x73, 0x69, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, + 0x12, 0x1b, 0x0a, 0x17, 0x4e, 0x4f, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x49, + 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x2a, 0x26, 0x0a, + 0x0d, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x09, + 0x0a, 0x05, 0x4e, 0x45, 0x56, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4c, 0x57, + 0x41, 0x59, 0x53, 0x10, 0x01, 0x32, 0xce, 0x0e, 0x0a, 0x13, 0x41, 0x70, 0x69, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6d, 0x0a, + 0x11, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, + 0x72, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, - 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, - 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2a, 0x36, 0x0a, - 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, - 0x0a, 0x07, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x52, - 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, - 0x4f, 0x57, 0x4e, 0x10, 0x02, 0x2a, 0x2c, 0x0a, 0x11, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, - 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, - 0x67, 0x10, 0x01, 0x2a, 0x26, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x0b, - 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, - 0x4f, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x01, 0x2a, 0x32, 0x0a, 0x13, 0x4b, - 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, - 0x61, 0x67, 0x12, 0x1b, 0x0a, 0x17, 0x4e, 0x4f, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x52, 0x55, 0x43, - 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x2a, - 0x26, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x12, 0x09, 0x0a, 0x05, 0x4e, 0x45, 0x56, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, - 0x4c, 0x57, 0x41, 0x59, 0x53, 0x10, 0x01, 0x32, 0xce, 0x0e, 0x0a, 0x13, 0x41, 0x70, 0x69, 0x43, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x6d, 0x0a, 0x11, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x12, 0x28, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, - 0x6c, 0x61, 0x72, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x2a, - 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x59, - 0x0a, 0x15, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, - 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x16, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x28, 0x01, 0x12, 0x6f, 0x0a, 0x12, 0x52, 0x75, 0x6e, - 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, - 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, - 0x61, 0x70, 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, - 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, - 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x0b, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x26, 0x2e, + 0x2e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x59, 0x0a, 0x15, + 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, + 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x00, 0x28, 0x01, 0x12, 0x6f, 0x0a, 0x12, 0x52, 0x75, 0x6e, 0x53, 0x74, + 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, - 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8d, 0x01, 0x0a, 0x2a, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x45, - 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, - 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, - 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0b, 0x45, 0x78, 0x65, 0x63, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, - 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x79, 0x0a, 0x22, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, - 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x39, 0x2e, 0x61, 0x70, 0x69, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x57, - 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x79, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, - 0x7b, 0x0a, 0x23, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, - 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x3a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, - 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, - 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x13, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, - 0x61, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, - 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, + 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x50, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, + 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x26, 0x2e, 0x61, 0x70, + 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8d, 0x01, 0x0a, 0x2a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, + 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, + 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x45, 0x2e, 0x61, + 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x41, 0x6e, 0x64, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0b, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x78, 0x65, + 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x79, 0x0a, 0x22, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, + 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, + 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x39, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x61, 0x69, + 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, + 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x7b, 0x0a, + 0x23, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, + 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x79, 0x12, 0x3a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, + 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x72, 0x67, 0x73, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x13, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x6f, 0x0a, - 0x15, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, - 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, - 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, - 0x41, 0x72, 0x67, 0x73, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, - 0x64, 0x44, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x79, - 0x0a, 0x15, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, - 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, - 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, - 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x74, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, 0x61, + 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x6f, 0x0a, 0x15, 0x44, + 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, + 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x41, 0x72, + 0x67, 0x73, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x44, + 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x79, 0x0a, 0x15, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, + 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, - 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1d, 0x53, 0x74, + 0x65, 0x62, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x41, + 0x72, 0x67, 0x73, 0x1a, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1d, 0x53, 0x74, 0x6f, 0x72, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, + 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x2e, 0x61, 0x70, 0x69, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, - 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x2e, 0x61, 0x70, - 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, - 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x72, 0x67, - 0x73, 0x1a, 0x38, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, - 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, - 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x12, - 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x39, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, - 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, - 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, - 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, - 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x61, 0x70, - 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, - 0x72, 0x67, 0x73, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x55, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, - 0x52, 0x75, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x29, 0x2e, 0x61, 0x70, + 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, + 0x38, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, + 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, 0x1e, 0x4c, + 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x12, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x39, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x41, 0x6e, 0x64, 0x55, 0x75, 0x69, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x91, 0x01, 0x0a, 0x1c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x52, 0x5a, 0x50, 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, 0x61, 0x70, - 0x69, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x6b, 0x75, - 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x70, 0x63, 0x5f, - 0x61, 0x70, 0x69, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x72, 0x74, 0x69, + 0x66, 0x61, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x61, 0x70, 0x69, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x41, 0x72, 0x67, + 0x73, 0x1a, 0x2a, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x55, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, + 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x29, 0x2e, 0x61, 0x70, 0x69, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x74, 0x61, 0x72, 0x6c, 0x61, 0x72, 0x6b, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x52, 0x5a, 0x50, 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, 0x61, 0x70, 0x69, 0x2f, + 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x6b, 0x75, 0x72, 0x74, + 0x6f, 0x73, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x70, 0x63, 0x5f, 0x61, 0x70, + 0x69, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc.pb.go b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc.pb.go index 780145bdc4..ca2ca2f836 100644 --- a/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc.pb.go +++ b/api/golang/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.24.4 +// - protoc v4.25.2 // source: api_container_service.proto package kurtosis_core_rpc_api_bindings diff --git a/api/golang/core/lib/binding_constructors/binding_constructors.go b/api/golang/core/lib/binding_constructors/binding_constructors.go index 9a6d8bc25a..ecc7c344e3 100644 --- a/api/golang/core/lib/binding_constructors/binding_constructors.go +++ b/api/golang/core/lib/binding_constructors/binding_constructors.go @@ -42,6 +42,7 @@ func NewRunStarlarkScriptArgs( cloudInstanceId string, cloudUserId string, imageDownloadMode kurtosis_core_rpc_api_bindings.ImageDownloadMode, + nonBlockingMode bool, ) *kurtosis_core_rpc_api_bindings.RunStarlarkScriptArgs { cloudInstanceIdCopy := new(string) *cloudInstanceIdCopy = cloudInstanceId @@ -61,6 +62,7 @@ func NewRunStarlarkScriptArgs( CloudInstanceId: cloudInstanceIdCopy, CloudUserId: cloudUserIdCopy, ImageDownloadMode: imageDownloadModeCopy, + NonBlockingMode: &nonBlockingMode, } } @@ -75,6 +77,7 @@ func NewRunStarlarkPackageArgs( cloudInstanceId string, cloudUserId string, imageDownloadMode kurtosis_core_rpc_api_bindings.ImageDownloadMode, + nonBlockingMode bool, ) *kurtosis_core_rpc_api_bindings.RunStarlarkPackageArgs { parallelismCopy := new(int32) *parallelismCopy = parallelism @@ -98,6 +101,7 @@ func NewRunStarlarkPackageArgs( CloudInstanceId: cloudInstanceIdCopy, CloudUserId: cloudUserIdCopy, ImageDownloadMode: imageDownloadModeCopy, + NonBlockingMode: &nonBlockingMode, } } @@ -112,6 +116,7 @@ func NewRunStarlarkRemotePackageArgs( cloudInstanceId string, cloudUserId string, imageDownloadMode kurtosis_core_rpc_api_bindings.ImageDownloadMode, + nonBlockingMode bool, ) *kurtosis_core_rpc_api_bindings.RunStarlarkPackageArgs { parallelismCopy := new(int32) *parallelismCopy = parallelism @@ -135,6 +140,7 @@ func NewRunStarlarkRemotePackageArgs( CloudInstanceId: cloudInstanceIdCopy, CloudUserId: cloudUserIdCopy, ImageDownloadMode: imageDownloadModeCopy, + NonBlockingMode: &nonBlockingMode, } } diff --git a/api/golang/core/lib/enclaves/enclave_context.go b/api/golang/core/lib/enclaves/enclave_context.go index 7fbffc2d41..91487cbb4a 100644 --- a/api/golang/core/lib/enclaves/enclave_context.go +++ b/api/golang/core/lib/enclaves/enclave_context.go @@ -110,7 +110,17 @@ func (enclaveCtx *EnclaveContext) RunStarlarkScript( return nil, nil, stacktrace.Propagate(err, "An error occurred when parsing YAML args for script '%v'", oldSerializedParams) } ctxWithCancel, cancelCtxFunc := context.WithCancel(ctx) - executeStartosisScriptArgs := binding_constructors.NewRunStarlarkScriptArgs(runConfig.MainFunctionName, serializedScript, serializedParams, runConfig.DryRun, runConfig.Parallelism, runConfig.ExperimentalFeatureFlags, runConfig.CloudInstanceId, runConfig.CloudUserId, runConfig.ImageDownload) + executeStartosisScriptArgs := binding_constructors.NewRunStarlarkScriptArgs( + runConfig.MainFunctionName, + serializedScript, + serializedParams, + runConfig.DryRun, + runConfig.Parallelism, + runConfig.ExperimentalFeatureFlags, + runConfig.CloudInstanceId, + runConfig.CloudUserId, + runConfig.ImageDownload, + runConfig.NonBlockingMode) starlarkResponseLineChan := make(chan *kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine) stream, err := enclaveCtx.client.RunStarlarkScript(ctxWithCancel, executeStartosisScriptArgs) @@ -172,7 +182,8 @@ func (enclaveCtx *EnclaveContext) RunStarlarkPackage( runConfig.ExperimentalFeatureFlags, runConfig.CloudInstanceId, runConfig.CloudUserId, - runConfig.ImageDownload) + runConfig.ImageDownload, + runConfig.NonBlockingMode) if err != nil { return nil, nil, stacktrace.Propagate(err, "Error preparing package '%s' for execution", packageRootPath) } @@ -297,7 +308,7 @@ func (enclaveCtx *EnclaveContext) RunStarlarkRemotePackage( }() starlarkResponseLineChan := make(chan *kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine) - executeStartosisScriptArgs := binding_constructors.NewRunStarlarkRemotePackageArgs(packageId, runConfig.RelativePathToMainFile, runConfig.MainFunctionName, serializedParams, runConfig.DryRun, runConfig.Parallelism, runConfig.ExperimentalFeatureFlags, runConfig.CloudInstanceId, runConfig.CloudUserId, runConfig.ImageDownload) + executeStartosisScriptArgs := binding_constructors.NewRunStarlarkRemotePackageArgs(packageId, runConfig.RelativePathToMainFile, runConfig.MainFunctionName, serializedParams, runConfig.DryRun, runConfig.Parallelism, runConfig.ExperimentalFeatureFlags, runConfig.CloudInstanceId, runConfig.CloudUserId, runConfig.ImageDownload, runConfig.NonBlockingMode) stream, err := enclaveCtx.client.RunStarlarkPackage(ctxWithCancel, executeStartosisScriptArgs) if err != nil { @@ -605,9 +616,21 @@ func (enclaveCtx *EnclaveContext) assembleRunStartosisPackageArg( cloudInstanceId string, cloudUserId string, imageDownloadMode kurtosis_core_rpc_api_bindings.ImageDownloadMode, + nonBlockingMode bool, ) (*kurtosis_core_rpc_api_bindings.RunStarlarkPackageArgs, error) { - return binding_constructors.NewRunStarlarkPackageArgs(packageName, relativePathToMainFile, mainFunctionName, serializedParams, dryRun, parallelism, experimentalFeatures, cloudInstanceId, cloudUserId, imageDownloadMode), nil + return binding_constructors.NewRunStarlarkPackageArgs( + packageName, + relativePathToMainFile, + mainFunctionName, + serializedParams, + dryRun, + parallelism, + experimentalFeatures, + cloudInstanceId, + cloudUserId, + imageDownloadMode, + nonBlockingMode), nil } func (enclaveCtx *EnclaveContext) uploadStarlarkPackage(packageId string, packageRootPath string) error { diff --git a/api/golang/core/lib/starlark_run_config/starlark_run_config.go b/api/golang/core/lib/starlark_run_config/starlark_run_config.go index ad1002473f..be8303b9c6 100644 --- a/api/golang/core/lib/starlark_run_config/starlark_run_config.go +++ b/api/golang/core/lib/starlark_run_config/starlark_run_config.go @@ -11,6 +11,7 @@ const ( defaultCloudInstanceId = "" defaultCloudUserId = "" defaultImageDownload = kurtosis_core_rpc_api_bindings.ImageDownloadMode_missing + defaultNonBlockingMode = false ) var defaultExperimentalFeatureFlags = []kurtosis_core_rpc_api_bindings.KurtosisFeatureFlag(nil) @@ -25,6 +26,7 @@ type StarlarkRunConfig struct { CloudInstanceId string CloudUserId string ImageDownload kurtosis_core_rpc_api_bindings.ImageDownloadMode + NonBlockingMode bool } type starlarkRunConfigOption func(*StarlarkRunConfig) @@ -40,6 +42,7 @@ func NewRunStarlarkConfig(opts ...starlarkRunConfigOption) *StarlarkRunConfig { CloudInstanceId: defaultCloudInstanceId, CloudUserId: defaultCloudUserId, ImageDownload: defaultImageDownload, + NonBlockingMode: defaultNonBlockingMode, } for _, opt := range opts { @@ -104,3 +107,9 @@ func WithImageDownloadMode(imageDownloadMode kurtosis_core_rpc_api_bindings.Imag config.ImageDownload = imageDownloadMode } } + +func WithNonBlockingMode(nonBlockingMode bool) starlarkRunConfigOption { + return func(config *StarlarkRunConfig) { + config.NonBlockingMode = nonBlockingMode + } +} diff --git a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go index e15990f41e..7420f88c38 100644 --- a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go +++ b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc-gen-go v1.32.0 +// protoc v4.25.2 // source: engine_service.proto package kurtosis_engine_rpc_api_bindings diff --git a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc.pb.go b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc.pb.go index 030fc811cf..d3a95bd5b6 100644 --- a/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc.pb.go +++ b/api/golang/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.24.4 +// - protoc v4.25.2 // source: engine_service.proto package kurtosis_engine_rpc_api_bindings diff --git a/api/golang/http_rest/api_types/api_types.gen.go b/api/golang/http_rest/api_types/api_types.gen.go index 877ee1b211..cdc4bfadf9 100644 --- a/api/golang/http_rest/api_types/api_types.gen.go +++ b/api/golang/http_rest/api_types/api_types.gen.go @@ -334,6 +334,9 @@ type RunStarlarkPackage struct { // MainFunctionName The name of the main function, the default value is "run" MainFunctionName *string `json:"main_function_name,omitempty"` + // NonBlockingMode Defaults to false + NonBlockingMode *bool `json:"non_blocking_mode,omitempty"` + // Parallelism Defaults to 4 Parallelism *int32 `json:"parallelism,omitempty"` @@ -363,6 +366,9 @@ type RunStarlarkScript struct { // MainFunctionName The name of the main function, the default value is "run" MainFunctionName *string `json:"main_function_name,omitempty"` + // NonBlockingMode Defaults to false + NonBlockingMode *bool `json:"non_blocking_mode,omitempty"` + // Parallelism Defaults to 4 Parallelism *int32 `json:"parallelism,omitempty"` diff --git a/api/golang/http_rest/server/core_rest_api/api_container_server.gen.go b/api/golang/http_rest/server/core_rest_api/api_container_server.gen.go index 8820011284..403877d2da 100644 --- a/api/golang/http_rest/server/core_rest_api/api_container_server.gen.go +++ b/api/golang/http_rest/server/core_rest_api/api_container_server.gen.go @@ -1546,83 +1546,83 @@ func (sh *strictHandler) PostEnclavesEnclaveIdentifierStarlarkScripts(ctx echo.C // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "fwAAAP//wAoBgRFnAAA=", + "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", + "CMTalCakb2r/p0Xq8/3/BwAA//8Jr2xbo2cAAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/openapi/specs/kurtosis_api.yaml b/api/openapi/specs/kurtosis_api.yaml index 150cc927bd..b271a458bf 100644 --- a/api/openapi/specs/kurtosis_api.yaml +++ b/api/openapi/specs/kurtosis_api.yaml @@ -1125,6 +1125,9 @@ components: description: Defaults to empty image_download_mode: $ref: "#/components/schemas/ImageDownloadMode" + non_blocking_mode: + type: boolean + description: Defaults to false required: - serialized_script @@ -1168,6 +1171,9 @@ components: description: Defaults to empty image_download_mode: $ref: "#/components/schemas/ImageDownloadMode" + non_blocking_mode: + type: boolean + description: Defaults to false KurtosisFeatureFlag: type: string diff --git a/api/protobuf/core/api_container_service.proto b/api/protobuf/core/api_container_service.proto index 9e53fa965b..01049313e3 100644 --- a/api/protobuf/core/api_container_service.proto +++ b/api/protobuf/core/api_container_service.proto @@ -170,6 +170,9 @@ message RunStarlarkScriptArgs { // Defaults to empty optional ImageDownloadMode image_download_mode = 9; + + // Defaults to false + optional bool non_blocking_mode = 10; } message RunStarlarkPackageArgs { @@ -217,6 +220,9 @@ message RunStarlarkPackageArgs { // Defaults to empty optional ImageDownloadMode image_download_mode = 14; + + // Defaults to false + optional bool non_blocking_mode = 15; } enum KurtosisFeatureFlag { diff --git a/api/rust/src/api_container_api.rs b/api/rust/src/api_container_api.rs index dbbb6c6480..494dfa631d 100644 --- a/api/rust/src/api_container_api.rs +++ b/api/rust/src/api_container_api.rs @@ -181,6 +181,9 @@ pub struct RunStarlarkScriptArgs { /// Defaults to empty #[prost(enumeration = "ImageDownloadMode", optional, tag = "9")] pub image_download_mode: ::core::option::Option, + /// Defaults to false + #[prost(bool, optional, tag = "10")] + pub non_blocking_mode: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -224,6 +227,9 @@ pub struct RunStarlarkPackageArgs { /// Defaults to empty #[prost(enumeration = "ImageDownloadMode", optional, tag = "14")] pub image_download_mode: ::core::option::Option, + /// Defaults to false + #[prost(bool, optional, tag = "15")] + pub non_blocking_mode: ::core::option::Option, /// Deprecated: If the package is local, it should have been uploaded with UploadStarlarkPackage prior to calling /// RunStarlarkPackage. If the package is remote and must be cloned within the APIC, use the standalone boolean flag /// clone_package below diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.d.ts index c7a6afdd19..f5505b0e0e 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.d.ts @@ -1,7 +1,7 @@ import * as grpcWeb from 'grpc-web'; -import * as api_container_service_pb from './api_container_service_pb'; // proto import: "api_container_service.proto" -import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; // proto import: "google/protobuf/empty.proto" +import * as api_container_service_pb from './api_container_service_pb'; +import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; export class ApiContainerServiceClient { diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.js b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.js index 403dc2af4e..07732d2fbc 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.js +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_grpc_web_pb.js @@ -6,7 +6,7 @@ // Code generated by protoc-gen-grpc-web. DO NOT EDIT. // versions: -// protoc-gen-grpc-web v1.5.0 +// protoc-gen-grpc-web v1.4.2 // protoc v3.19.1 // source: api_container_service.proto diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts index d4d830a5e7..13742313fe 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.d.ts @@ -1,6 +1,6 @@ import * as jspb from 'google-protobuf' -import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; // proto import: "google/protobuf/empty.proto" +import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; export class Port extends jspb.Message { @@ -179,6 +179,11 @@ export class RunStarlarkScriptArgs extends jspb.Message { hasImageDownloadMode(): boolean; clearImageDownloadMode(): RunStarlarkScriptArgs; + getNonBlockingMode(): boolean; + setNonBlockingMode(value: boolean): RunStarlarkScriptArgs; + hasNonBlockingMode(): boolean; + clearNonBlockingMode(): RunStarlarkScriptArgs; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): RunStarlarkScriptArgs.AsObject; static toObject(includeInstance: boolean, msg: RunStarlarkScriptArgs): RunStarlarkScriptArgs.AsObject; @@ -198,6 +203,7 @@ export namespace RunStarlarkScriptArgs { cloudInstanceId?: string, cloudUserId?: string, imageDownloadMode?: ImageDownloadMode, + nonBlockingMode?: boolean, } export enum SerializedParamsCase { @@ -234,6 +240,11 @@ export namespace RunStarlarkScriptArgs { _IMAGE_DOWNLOAD_MODE_NOT_SET = 0, IMAGE_DOWNLOAD_MODE = 9, } + + export enum NonBlockingModeCase { + _NON_BLOCKING_MODE_NOT_SET = 0, + NON_BLOCKING_MODE = 10, + } } export class RunStarlarkPackageArgs extends jspb.Message { @@ -298,6 +309,11 @@ export class RunStarlarkPackageArgs extends jspb.Message { hasImageDownloadMode(): boolean; clearImageDownloadMode(): RunStarlarkPackageArgs; + getNonBlockingMode(): boolean; + setNonBlockingMode(value: boolean): RunStarlarkPackageArgs; + hasNonBlockingMode(): boolean; + clearNonBlockingMode(): RunStarlarkPackageArgs; + getStarlarkPackageContentCase(): RunStarlarkPackageArgs.StarlarkPackageContentCase; serializeBinary(): Uint8Array; @@ -323,6 +339,7 @@ export namespace RunStarlarkPackageArgs { cloudInstanceId?: string, cloudUserId?: string, imageDownloadMode?: ImageDownloadMode, + nonBlockingMode?: boolean, } export enum StarlarkPackageContentCase { @@ -375,6 +392,11 @@ export namespace RunStarlarkPackageArgs { _IMAGE_DOWNLOAD_MODE_NOT_SET = 0, IMAGE_DOWNLOAD_MODE = 14, } + + export enum NonBlockingModeCase { + _NON_BLOCKING_MODE_NOT_SET = 0, + NON_BLOCKING_MODE = 15, + } } export class StarlarkRunResponseLine extends jspb.Message { diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.js b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.js index 94eef3a39c..49174b1033 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.js +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/api_container_service_pb.js @@ -1937,7 +1937,8 @@ proto.api_container_api.RunStarlarkScriptArgs.toObject = function(includeInstanc experimentalFeaturesList: (f = jspb.Message.getRepeatedField(msg, 6)) == null ? undefined : f, cloudInstanceId: jspb.Message.getFieldWithDefault(msg, 7, ""), cloudUserId: jspb.Message.getFieldWithDefault(msg, 8, ""), - imageDownloadMode: jspb.Message.getFieldWithDefault(msg, 9, 0) + imageDownloadMode: jspb.Message.getFieldWithDefault(msg, 9, 0), + nonBlockingMode: jspb.Message.getBooleanFieldWithDefault(msg, 10, false) }; if (includeInstance) { @@ -2012,6 +2013,10 @@ proto.api_container_api.RunStarlarkScriptArgs.deserializeBinaryFromReader = func var value = /** @type {!proto.api_container_api.ImageDownloadMode} */ (reader.readEnum()); msg.setImageDownloadMode(value); break; + case 10: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setNonBlockingMode(value); + break; default: reader.skipField(); break; @@ -2104,6 +2109,13 @@ proto.api_container_api.RunStarlarkScriptArgs.serializeBinaryToWriter = function f ); } + f = /** @type {boolean} */ (jspb.Message.getField(message, 10)); + if (f != null) { + writer.writeBool( + 10, + f + ); + } }; @@ -2414,6 +2426,42 @@ proto.api_container_api.RunStarlarkScriptArgs.prototype.hasImageDownloadMode = f }; +/** + * optional bool non_blocking_mode = 10; + * @return {boolean} + */ +proto.api_container_api.RunStarlarkScriptArgs.prototype.getNonBlockingMode = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 10, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.api_container_api.RunStarlarkScriptArgs} returns this + */ +proto.api_container_api.RunStarlarkScriptArgs.prototype.setNonBlockingMode = function(value) { + return jspb.Message.setField(this, 10, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.api_container_api.RunStarlarkScriptArgs} returns this + */ +proto.api_container_api.RunStarlarkScriptArgs.prototype.clearNonBlockingMode = function() { + return jspb.Message.setField(this, 10, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.api_container_api.RunStarlarkScriptArgs.prototype.hasNonBlockingMode = function() { + return jspb.Message.getField(this, 10) != null; +}; + + /** * List of repeated fields within this message type. @@ -2491,7 +2539,8 @@ proto.api_container_api.RunStarlarkPackageArgs.toObject = function(includeInstan experimentalFeaturesList: (f = jspb.Message.getRepeatedField(msg, 11)) == null ? undefined : f, cloudInstanceId: jspb.Message.getFieldWithDefault(msg, 12, ""), cloudUserId: jspb.Message.getFieldWithDefault(msg, 13, ""), - imageDownloadMode: jspb.Message.getFieldWithDefault(msg, 14, 0) + imageDownloadMode: jspb.Message.getFieldWithDefault(msg, 14, 0), + nonBlockingMode: jspb.Message.getBooleanFieldWithDefault(msg, 15, false) }; if (includeInstance) { @@ -2582,6 +2631,10 @@ proto.api_container_api.RunStarlarkPackageArgs.deserializeBinaryFromReader = fun var value = /** @type {!proto.api_container_api.ImageDownloadMode} */ (reader.readEnum()); msg.setImageDownloadMode(value); break; + case 15: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setNonBlockingMode(value); + break; default: reader.skipField(); break; @@ -2702,6 +2755,13 @@ proto.api_container_api.RunStarlarkPackageArgs.serializeBinaryToWriter = functio f ); } + f = /** @type {boolean} */ (jspb.Message.getField(message, 15)); + if (f != null) { + writer.writeBool( + 15, + f + ); + } }; @@ -3180,6 +3240,42 @@ proto.api_container_api.RunStarlarkPackageArgs.prototype.hasImageDownloadMode = }; +/** + * optional bool non_blocking_mode = 15; + * @return {boolean} + */ +proto.api_container_api.RunStarlarkPackageArgs.prototype.getNonBlockingMode = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 15, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.api_container_api.RunStarlarkPackageArgs} returns this + */ +proto.api_container_api.RunStarlarkPackageArgs.prototype.setNonBlockingMode = function(value) { + return jspb.Message.setField(this, 15, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.api_container_api.RunStarlarkPackageArgs} returns this + */ +proto.api_container_api.RunStarlarkPackageArgs.prototype.clearNonBlockingMode = function() { + return jspb.Message.setField(this, 15, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.api_container_api.RunStarlarkPackageArgs.prototype.hasNonBlockingMode = function() { + return jspb.Message.getField(this, 15) != null; +}; + + /** * Oneof group definitions for this message. Each group defines the field diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.d.ts index 6441630968..201f76f184 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.d.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-connect-es v0.13.2 with parameter "target=js+dts" +// @generated by protoc-gen-connect-es v1.3.0 with parameter "target=js+dts" // @generated from file api_container_service.proto (package api_container_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.js b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.js index fc66916490..627620d9d8 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.js +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_connect.js @@ -1,4 +1,4 @@ -// @generated by protoc-gen-connect-es v0.13.2 with parameter "target=js+dts" +// @generated by protoc-gen-connect-es v1.3.0 with parameter "target=js+dts" // @generated from file api_container_service.proto (package api_container_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts index f230ed1e42..b45831ded6 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.d.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v1.5.1 with parameter "target=js+dts" +// @generated by protoc-gen-es v1.3.1 with parameter "target=js+dts" // @generated from file api_container_service.proto (package api_container_api, syntax proto3) /* eslint-disable */ // @ts-nocheck @@ -368,6 +368,13 @@ export declare class RunStarlarkScriptArgs extends Message); static readonly runtime: typeof proto3; @@ -491,6 +498,13 @@ export declare class RunStarlarkPackageArgs extends Message); static readonly runtime: typeof proto3; diff --git a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js index 6aeefe438c..dd244aa275 100644 --- a/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js +++ b/api/typescript/src/core/kurtosis_core_rpc_api_bindings/connect/api_container_service_pb.js @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v1.5.1 with parameter "target=js+dts" +// @generated by protoc-gen-es v1.3.1 with parameter "target=js+dts" // @generated from file api_container_service.proto (package api_container_api, syntax proto3) /* eslint-disable */ // @ts-nocheck @@ -150,6 +150,7 @@ export const RunStarlarkScriptArgs = proto3.makeMessageType( { no: 7, name: "cloud_instance_id", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 8, name: "cloud_user_id", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 9, name: "image_download_mode", kind: "enum", T: proto3.getEnumType(ImageDownloadMode), opt: true }, + { no: 10, name: "non_blocking_mode", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, ], ); @@ -172,6 +173,7 @@ export const RunStarlarkPackageArgs = proto3.makeMessageType( { no: 12, name: "cloud_instance_id", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 13, name: "cloud_user_id", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 14, name: "image_download_mode", kind: "enum", T: proto3.getEnumType(ImageDownloadMode), opt: true }, + { no: 15, name: "non_blocking_mode", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, ], ); diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.d.ts index 04235e5f69..49db57cc50 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.d.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-connect-es v0.13.2 with parameter "target=js+dts" +// @generated by protoc-gen-connect-es v1.3.0 with parameter "target=js+dts" // @generated from file engine_service.proto (package engine_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.js b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.js index 7d45190ac5..e71a470bc4 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.js +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_connect.js @@ -1,4 +1,4 @@ -// @generated by protoc-gen-connect-es v0.13.2 with parameter "target=js+dts" +// @generated by protoc-gen-connect-es v1.3.0 with parameter "target=js+dts" // @generated from file engine_service.proto (package engine_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts index 269a10fde9..7eb383b09a 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.d.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v1.5.1 with parameter "target=js+dts" +// @generated by protoc-gen-es v1.3.1 with parameter "target=js+dts" // @generated from file engine_service.proto (package engine_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js index 24c994a6f4..c245f62078 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/connect/engine_service_pb.js @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v1.5.1 with parameter "target=js+dts" +// @generated by protoc-gen-es v1.3.1 with parameter "target=js+dts" // @generated from file engine_service.proto (package engine_api, syntax proto3) /* eslint-disable */ // @ts-nocheck diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.d.ts index 4a41f55da9..523a2a2d20 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.d.ts @@ -1,7 +1,7 @@ import * as grpcWeb from 'grpc-web'; -import * as engine_service_pb from './engine_service_pb'; // proto import: "engine_service.proto" -import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; // proto import: "google/protobuf/empty.proto" +import * as engine_service_pb from './engine_service_pb'; +import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; export class EngineServiceClient { diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.js b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.js index 6a8a4bad27..6a18e31707 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.js +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb.js @@ -6,7 +6,7 @@ // Code generated by protoc-gen-grpc-web. DO NOT EDIT. // versions: -// protoc-gen-grpc-web v1.5.0 +// protoc-gen-grpc-web v1.4.2 // protoc v3.19.1 // source: engine_service.proto diff --git a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts index 06fc96768c..29cffdefcd 100644 --- a/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts +++ b/api/typescript/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_pb.d.ts @@ -1,7 +1,7 @@ import * as jspb from 'google-protobuf' -import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; // proto import: "google/protobuf/empty.proto" -import * as google_protobuf_timestamp_pb from 'google-protobuf/google/protobuf/timestamp_pb'; // proto import: "google/protobuf/timestamp.proto" +import * as google_protobuf_empty_pb from 'google-protobuf/google/protobuf/empty_pb'; +import * as google_protobuf_timestamp_pb from 'google-protobuf/google/protobuf/timestamp_pb'; export class GetEngineInfoResponse extends jspb.Message { diff --git a/api/typescript/src/engine/rest_api_bindings/types.d.ts b/api/typescript/src/engine/rest_api_bindings/types.d.ts index 4764b775d6..1e390f00c9 100644 --- a/api/typescript/src/engine/rest_api_bindings/types.d.ts +++ b/api/typescript/src/engine/rest_api_bindings/types.d.ts @@ -1326,6 +1326,8 @@ export interface components { /** @description Defaults to empty */ cloud_user_id?: string; image_download_mode?: components["schemas"]["ImageDownloadMode"]; + /** @description Defaults to false */ + non_blocking_mode?: boolean; }; RunStarlarkPackage: { /** @description Parameters data for the Starlark package main function */ @@ -1354,6 +1356,8 @@ export interface components { /** @description Defaults to empty */ cloud_user_id?: string; image_download_mode?: components["schemas"]["ImageDownloadMode"]; + /** @description Defaults to false */ + non_blocking_mode?: boolean; }; /** * @description 0 - NO_INSTRUCTIONS_CACHING diff --git a/cli/cli/commands/run/run.go b/cli/cli/commands/run/run.go index 9d3bc3d3e0..c751d50c91 100644 --- a/cli/cli/commands/run/run.go +++ b/cli/cli/commands/run/run.go @@ -103,6 +103,9 @@ const ( imageDownloadFlagKey = "image-download" defaultImageDownload = "missing" + nonBlockingModeFlagKey = "non-blocking-tasks" + defaultBlockingMode = "false" + httpProtocolRegexStr = "^(http|https)://" ) @@ -204,6 +207,12 @@ var StarlarkRunCmd = &engine_consuming_kurtosis_command.EngineConsumingKurtosisC Type: flags.FlagType_String, Default: defaultImageDownload, }, + { + Key: nonBlockingModeFlagKey, + Usage: "If set, Kurtosis will not block on removing services from tasks from run_sh and run_python instructions. These services will remain and must be manually cleaned up.", + Type: flags.FlagType_Bool, + Default: defaultBlockingMode, + }, }, Args: []*args.ArgConfig{ // TODO add a `Usage` description here when ArgConfig supports it @@ -309,6 +318,11 @@ func run( return stacktrace.Propagate(err, "Expected a value for the '%v' flag but failed to get it", packageArgsFileFlagKey) } + nonBlockingMode, err := flags.GetBool(nonBlockingModeFlagKey) + if err != nil { + return stacktrace.Propagate(err, "Expected a value for the '%v' flag but failed to get it", nonBlockingModeFlagKey) + } + if packageArgs == inputArgsAreEmptyBracesByDefault && packageArgsFile != packageArgsFileDefaultValue { logrus.Debugf("'%v' is empty but '%v' is provided so we will go with the '%v' value", inputArgsArgKey, packageArgsFileFlagKey, packageArgsFileFlagKey) packageArgs, err = getArgsFromFilepathOrURL(packageArgsFile) @@ -327,6 +341,7 @@ func run( starlark_run_config.WithRelativePathToMainFile(relativePathToTheMainFile), starlark_run_config.WithSerializedParams(packageArgs), starlark_run_config.WithImageDownloadMode(*imageDownload), + starlark_run_config.WithNonBlockingMode(nonBlockingMode), ) kurtosisCtx, err := kurtosis_context.NewKurtosisContextFromLocalEngine() diff --git a/core/server/api_container/server/api_container_service.go b/core/server/api_container/server/api_container_service.go index 7fbd5af348..74d8bb5f2b 100644 --- a/core/server/api_container/server/api_container_service.go +++ b/core/server/api_container/server/api_container_service.go @@ -146,7 +146,7 @@ func (apicService *ApiContainerService) RunStarlarkScript(args *kurtosis_core_rp mainFuncName := args.GetMainFunctionName() experimentalFeatures := args.GetExperimentalFeatures() ApiDownloadMode := shared_utils.GetOrDefault(args.ImageDownloadMode, defaultImageDownloadMode) - + nonBlockingMode := args.GetNonBlockingMode() downloadMode := convertFromImageDownloadModeAPI(ApiDownloadMode) metricsErr := apicService.metricsClient.TrackKurtosisRun(startosis_constants.PackageIdPlaceholderForStandaloneScript, isNotRemote, dryRun, isScript) @@ -155,7 +155,19 @@ func (apicService *ApiContainerService) RunStarlarkScript(args *kurtosis_core_rp } noPackageReplaceOptions := map[string]string{} - apicService.runStarlark(parallelism, dryRun, startosis_constants.PackageIdPlaceholderForStandaloneScript, noPackageReplaceOptions, mainFuncName, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, serializedStarlarkScript, serializedParams, downloadMode, args.GetExperimentalFeatures(), stream) + apicService.runStarlark( + parallelism, + dryRun, + startosis_constants.PackageIdPlaceholderForStandaloneScript, + noPackageReplaceOptions, + mainFuncName, + startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, + serializedStarlarkScript, + serializedParams, + downloadMode, + nonBlockingMode, + args.GetExperimentalFeatures(), + stream) apicService.starlarkRun = &kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse{ PackageId: startosis_constants.PackageIdPlaceholderForStandaloneScript, @@ -248,6 +260,7 @@ func (apicService *ApiContainerService) RunStarlarkPackage(args *kurtosis_core_r mainFuncName := args.GetMainFunctionName() ApiDownloadMode := shared_utils.GetOrDefault(args.ImageDownloadMode, defaultImageDownloadMode) downloadMode := convertFromImageDownloadModeAPI(ApiDownloadMode) + nonBlockignMode := args.GetNonBlockingMode() var scriptWithRunFunction string var interpretationError *startosis_errors.InterpretationError @@ -280,7 +293,7 @@ func (apicService *ApiContainerService) RunStarlarkPackage(args *kurtosis_core_r if metricsErr != nil { logrus.Warn("An error occurred tracking kurtosis run event") } - apicService.runStarlark(parallelism, dryRun, detectedPackageId, detectedPackageReplaceOptions, mainFuncName, actualRelativePathToMainFile, scriptWithRunFunction, serializedParams, downloadMode, args.ExperimentalFeatures, stream) + apicService.runStarlark(parallelism, dryRun, detectedPackageId, detectedPackageReplaceOptions, mainFuncName, actualRelativePathToMainFile, scriptWithRunFunction, serializedParams, downloadMode, nonBlockignMode, args.ExperimentalFeatures, stream) apicService.starlarkRun = &kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse{ PackageId: packageIdFromArgs, @@ -823,10 +836,11 @@ func (apicService *ApiContainerService) runStarlark( serializedStarlark string, serializedParams string, imageDownloadMode image_download_mode.ImageDownloadMode, + nonBlockingMode bool, experimentalFeatures []kurtosis_core_rpc_api_bindings.KurtosisFeatureFlag, stream grpc.ServerStream, ) { - responseLineStream := apicService.startosisRunner.Run(stream.Context(), dryRun, parallelism, packageId, packageReplaceOptions, mainFunctionName, relativePathToMainFile, serializedStarlark, serializedParams, imageDownloadMode, experimentalFeatures) + responseLineStream := apicService.startosisRunner.Run(stream.Context(), dryRun, parallelism, packageId, packageReplaceOptions, mainFunctionName, relativePathToMainFile, serializedStarlark, serializedParams, imageDownloadMode, nonBlockingMode, experimentalFeatures) for { select { case <-stream.Context().Done(): diff --git a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go index 5aeae0154e..1afae0c723 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go @@ -59,6 +59,7 @@ func KurtosisPlanInstructions( runtimeValueStore *runtime_value_store.RuntimeValueStore, packageContentProvider startosis_packages.PackageContentProvider, packageReplaceOptions map[string]string, + nonBlockingMode bool, ) []*kurtosis_plan_instruction.KurtosisPlanInstruction { return []*kurtosis_plan_instruction.KurtosisPlanInstruction{ add_service.NewAddService(serviceNetwork, runtimeValueStore, packageId, packageContentProvider, packageReplaceOptions), @@ -70,8 +71,8 @@ func KurtosisPlanInstructions( render_templates.NewRenderTemplatesInstruction(serviceNetwork, runtimeValueStore), request.NewRequest(serviceNetwork, runtimeValueStore), start_service.NewStartService(serviceNetwork), - tasks.NewRunPythonService(serviceNetwork, runtimeValueStore), - tasks.NewRunShService(serviceNetwork, runtimeValueStore), + tasks.NewRunPythonService(serviceNetwork, runtimeValueStore, nonBlockingMode), + tasks.NewRunShService(serviceNetwork, runtimeValueStore, nonBlockingMode), stop_service.NewStopService(serviceNetwork), store_service_files.NewStoreServiceFiles(serviceNetwork), upload_files.NewUploadFiles(packageId, serviceNetwork, packageContentProvider, packageReplaceOptions), diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go index 5e500f2eac..e23aa1e328 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go @@ -40,7 +40,7 @@ const ( successfulPipRunExitCode = 0 ) -func NewRunPythonService(serviceNetwork service_network.ServiceNetwork, runtimeValueStore *runtime_value_store.RuntimeValueStore) *kurtosis_plan_instruction.KurtosisPlanInstruction { +func NewRunPythonService(serviceNetwork service_network.ServiceNetwork, runtimeValueStore *runtime_value_store.RuntimeValueStore, nonBlockingMode bool) *kurtosis_plan_instruction.KurtosisPlanInstruction { return &kurtosis_plan_instruction.KurtosisPlanInstruction{ KurtosisBaseBuiltin: &kurtosis_starlark_framework.KurtosisBaseBuiltin{ Name: RunPythonBuiltinName, @@ -100,6 +100,7 @@ func NewRunPythonService(serviceNetwork service_network.ServiceNetwork, runtimeV pythonArguments: nil, packages: nil, name: "", + nonBlockingMode: nonBlockingMode, serviceConfig: nil, // populated at interpretation time run: "", // populated at interpretation time resultUuid: "", // populated at interpretation time @@ -124,9 +125,10 @@ type RunPythonCapabilities struct { runtimeValueStore *runtime_value_store.RuntimeValueStore serviceNetwork service_network.ServiceNetwork - resultUuid string - name string - run string + resultUuid string + name string + run string + nonBlockingMode bool pythonArguments []string packages []string @@ -303,8 +305,12 @@ func (builtin *RunPythonCapabilities) Execute(ctx context.Context, _ *builtin_ar } } - if err = removeService(ctx, builtin.serviceNetwork, builtin.name); err != nil { - return "", stacktrace.Propagate(err, "attempted to remove the temporary task container but failed") + // If the user indicated not to block on removing services after tasks, don't remove the service. + // The user will have to remove the task service themselves or it will get cleaned up with Kurtosis clean. + if !builtin.nonBlockingMode { + if err = removeService(ctx, builtin.serviceNetwork, builtin.name); err != nil { + return "", stacktrace.Propagate(err, "attempted to remove the temporary task container but failed") + } } return instructionResult, err diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go index 91e4ff12fb..4935dacb60 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go @@ -29,7 +29,7 @@ const ( defaultRunShImageName = "badouralix/curl-jq" ) -func NewRunShService(serviceNetwork service_network.ServiceNetwork, runtimeValueStore *runtime_value_store.RuntimeValueStore) *kurtosis_plan_instruction.KurtosisPlanInstruction { +func NewRunShService(serviceNetwork service_network.ServiceNetwork, runtimeValueStore *runtime_value_store.RuntimeValueStore, nonBlockingMode bool) *kurtosis_plan_instruction.KurtosisPlanInstruction { return &kurtosis_plan_instruction.KurtosisPlanInstruction{ KurtosisBaseBuiltin: &kurtosis_starlark_framework.KurtosisBaseBuiltin{ Name: RunShBuiltinName, @@ -82,6 +82,7 @@ func NewRunShService(serviceNetwork service_network.ServiceNetwork, runtimeValue serviceNetwork: serviceNetwork, runtimeValueStore: runtimeValueStore, name: "", + nonBlockingMode: nonBlockingMode, serviceConfig: nil, // populated at interpretation time run: "", // populated at interpretation time resultUuid: "", // populated at interpretation time @@ -105,9 +106,10 @@ type RunShCapabilities struct { runtimeValueStore *runtime_value_store.RuntimeValueStore serviceNetwork service_network.ServiceNetwork - resultUuid string - name string - run string + resultUuid string + name string + run string + nonBlockingMode bool serviceConfig *service.ServiceConfig storeSpecList []*store_spec.StoreSpec @@ -248,8 +250,12 @@ func (builtin *RunShCapabilities) Execute(ctx context.Context, _ *builtin_argume } } - if err = removeService(ctx, builtin.serviceNetwork, builtin.name); err != nil { - return "", stacktrace.Propagate(err, "attempted to remove the temporary task container but failed") + // If the user indicated not to block on removing services after tasks, don't remove the service. + // The user will have to remove the task service themselves, or it will get cleaned up with Kurtosis clean. + if !builtin.nonBlockingMode { + if err = removeService(ctx, builtin.serviceNetwork, builtin.name); err != nil { + return "", stacktrace.Propagate(err, "attempted to remove the temporary task container but failed") + } } return instructionResult, err diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go index 2632a5eb6c..9e683100bf 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/tasks_shared.go @@ -297,7 +297,7 @@ func formatErrorMessage(errorMessage string, errorFromExec string) string { func removeService(ctx context.Context, serviceNetwork service_network.ServiceNetwork, serviceName string) error { _, err := serviceNetwork.RemoveService(ctx, serviceName) if err != nil { - return stacktrace.NewError("error occurred while removing task with name %v", serviceName) + return stacktrace.Propagate(err, "error occurred while removing task with name %v", serviceName) } return nil } 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 ca9f7e1c09..ed23c5bd73 100644 --- a/core/server/api_container/server/startosis_engine/startosis_interpreter.go +++ b/core/server/api_container/server/startosis_engine/startosis_interpreter.go @@ -90,6 +90,7 @@ func (interpreter *StartosisInterpreter) InterpretAndOptimizePlan( relativePathtoMainFile string, serializedStarlark string, serializedJsonParams string, + nonBlockingMode bool, currentEnclavePlan *enclave_plan_persistence.EnclavePlan, ) (string, *instructions_plan.InstructionsPlan, *kurtosis_core_rpc_api_bindings.StarlarkInterpretationError) { @@ -100,7 +101,7 @@ func (interpreter *StartosisInterpreter) InterpretAndOptimizePlan( // run interpretation with no mask at all to generate the list of instructions as if the enclave was empty enclaveComponents := enclave_structure.NewEnclaveComponents() emptyPlanInstructionsMask := resolver.NewInstructionsPlanMask(0) - naiveInstructionsPlanSerializedScriptOutput, naiveInstructionsPlan, interpretationErrorApi := interpreter.Interpret(ctx, packageId, mainFunctionName, packageReplaceOptions, relativePathtoMainFile, serializedStarlark, serializedJsonParams, enclaveComponents, emptyPlanInstructionsMask) + naiveInstructionsPlanSerializedScriptOutput, naiveInstructionsPlan, interpretationErrorApi := interpreter.Interpret(ctx, packageId, mainFunctionName, packageReplaceOptions, relativePathtoMainFile, serializedStarlark, serializedJsonParams, nonBlockingMode, enclaveComponents, emptyPlanInstructionsMask) if interpretationErrorApi != nil { return startosis_constants.NoOutputObject, nil, interpretationErrorApi } @@ -170,7 +171,7 @@ func (interpreter *StartosisInterpreter) InterpretAndOptimizePlan( } // Now that we have a potential plan mask, try running interpretation again using this plan mask - attemptSerializedScriptOutput, attemptInstructionsPlan, interpretationErrorApi := interpreter.Interpret(ctx, packageId, mainFunctionName, packageReplaceOptions, relativePathtoMainFile, serializedStarlark, serializedJsonParams, enclaveComponents, potentialMask) + attemptSerializedScriptOutput, attemptInstructionsPlan, interpretationErrorApi := interpreter.Interpret(ctx, packageId, mainFunctionName, packageReplaceOptions, relativePathtoMainFile, serializedStarlark, serializedJsonParams, nonBlockingMode, enclaveComponents, potentialMask) if interpretationErrorApi != nil { // Note: there's no real reason why this interpretation would fail with an error, given that the package // has been interpreted once already (right above). But to be on the safe side, check the error @@ -218,6 +219,7 @@ func (interpreter *StartosisInterpreter) Interpret( relativePathtoMainFile string, serializedStarlark string, serializedJsonParams string, + nonBlockingMode bool, enclaveComponents *enclave_structure.EnclaveComponents, instructionsPlanMask *resolver.InstructionsPlanMask, ) (string, *instructions_plan.InstructionsPlan, *kurtosis_core_rpc_api_bindings.StarlarkInterpretationError) { @@ -268,7 +270,7 @@ func (interpreter *StartosisInterpreter) Interpret( if mainFuncParamsNum >= minimumParamsRequiredForPlan { firstParamName, _ := mainFunction.Param(planParamIndex) if firstParamName == planParamName { - kurtosisPlanInstructions := KurtosisPlanInstructions(packageId, interpreter.serviceNetwork, interpreter.recipeExecutor, interpreter.packageContentProvider, packageReplaceOptions) + kurtosisPlanInstructions := KurtosisPlanInstructions(packageId, interpreter.serviceNetwork, interpreter.recipeExecutor, interpreter.packageContentProvider, packageReplaceOptions, nonBlockingMode) planModule := plan_module.PlanModule(newInstructionsPlan, enclaveComponents, interpreter.starlarkValueSerde, instructionsPlanMask, kurtosisPlanInstructions) argsTuple = append(argsTuple, planModule) } 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 9509ec817a..76407536c5 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 @@ -28,6 +28,8 @@ const ( enclaveUuid = enclave.EnclaveUUID("enclave-uuid") noInputParams = "{}" + + defaultNonBlockingMode = false ) var noPackageReplaceOptions = map[string]string{} @@ -95,6 +97,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_I startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, noInputParams, + defaultNonBlockingMode, enclave_structure.NewEnclaveComponents(), resolver.NewInstructionsPlanMask(0)) require.Nil(suite.T(), interpretationApiErr) @@ -110,6 +113,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_I startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, noInputParams, + defaultNonBlockingMode, convertedEnclavePlan, ) require.Nil(suite.T(), interpretationError) @@ -150,6 +154,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_A startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, initialScript, noInputParams, + defaultNonBlockingMode, enclave_structure.NewEnclaveComponents(), resolver.NewInstructionsPlanMask(0)) require.Nil(suite.T(), interpretationApiErr) @@ -170,6 +175,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_A startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, updatedScript, noInputParams, + defaultNonBlockingMode, convertedEnclavePlan, ) require.Nil(suite.T(), interpretationError) @@ -211,6 +217,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_D startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, initialScript, noInputParams, + defaultNonBlockingMode, enclave_structure.NewEnclaveComponents(), resolver.NewInstructionsPlanMask(0)) require.Nil(suite.T(), interpretationApiErr) @@ -229,6 +236,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_D startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, updatedScript, noInputParams, + defaultNonBlockingMode, convertedEnclavePlan, ) require.Nil(suite.T(), interpretationError) @@ -264,6 +272,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_I startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, initialScript, noInputParams, + defaultNonBlockingMode, enclave_structure.NewEnclaveComponents(), resolver.NewInstructionsPlanMask(0)) require.Nil(suite.T(), interpretationApiErr) @@ -284,6 +293,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_I startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, updatedScript, noInputParams, + defaultNonBlockingMode, convertedEnclavePlan, ) require.Nil(suite.T(), interpretationError) @@ -325,6 +335,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_A startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, initialScript, noInputParams, + defaultNonBlockingMode, enclave_structure.NewEnclaveComponents(), resolver.NewInstructionsPlanMask(0)) require.Nil(suite.T(), interpretationApiErr) @@ -346,6 +357,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_A startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, updatedScript, noInputParams, + defaultNonBlockingMode, convertedEnclavePlan, ) require.Nil(suite.T(), interpretationError) @@ -396,6 +408,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_U startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, initialScript, noInputParams, + defaultNonBlockingMode, enclave_structure.NewEnclaveComponents(), resolver.NewInstructionsPlanMask(0)) require.Nil(suite.T(), interpretationApiErr) @@ -422,6 +435,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) TestInterpretAndOptimize_U startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, updatedScript, noInputParams, + defaultNonBlockingMode, convertedEnclavePlan, ) require.Nil(suite.T(), interpretationError) 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 5963725fe6..4f560c25b3 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 @@ -100,7 +100,7 @@ def run(plan): plan.print("` + testString + `") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 1, instructionsPlan.Size()) // Only the print statement @@ -121,7 +121,7 @@ def deploy_contract(plan,service_name,contract_name,init_message,args): mainFunctionName := "deploy_contract" inputArgs := `{"service_name": "my-service", "contract_name": "my-contract", "init_message": "Init message", "args": {"arg1": "arg1-value", "arg2": "arg2-value"}}` - result, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, mainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, inputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + result, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, mainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, inputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 3, instructionsPlan.Size()) // The three print functions require.NotNil(suite.T(), result) @@ -142,7 +142,7 @@ def my_func(my_arg1, my_arg2, args): mainFunctionName := "my_func" inputArgs := `{"my_arg1": "foo", "my_arg2": "bar", "args": {"arg1": "arg1-value", "arg2": "arg2-value"}}` - result, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, mainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, inputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + result, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, mainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, inputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 0, instructionsPlan.Size()) // There are no instructions to execute require.NotNil(suite.T(), result) @@ -159,7 +159,7 @@ def run(plan): plan.print(my_dict) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 2, instructionsPlan.Size()) @@ -177,7 +177,7 @@ def run(plan): unknownInstruction() ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) expectedError := startosis_errors.NewInterpretationErrorWithCustomMsg( []startosis_errors.CallFrame{ @@ -198,7 +198,7 @@ unknownVariable unknownInstruction2() ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) expectedError := startosis_errors.NewInterpretationErrorWithCustomMsg( []startosis_errors.CallFrame{ @@ -220,7 +220,7 @@ def run(): load("otherScript.start") # fails b/c load takes in at least 2 args ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) expectedError := startosis_errors.NewInterpretationErrorFromStacktrace( []startosis_errors.CallFrame{ @@ -252,7 +252,7 @@ def run(plan): plan.print("The grpc transport protocol is " + datastore_service.ports["grpc"].transport_protocol) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, fmt.Sprintf(script, testServiceName), startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, fmt.Sprintf(script, testServiceName), startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 5, instructionsPlan.Size()) @@ -287,7 +287,7 @@ def run(plan): plan.print("The transport protocol is " + datastore_service.ports["grpc"].transport_protocol) plan.print("The application protocol is " + datastore_service.ports["grpc"].application_protocol) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, fmt.Sprintf(script, testServiceName), startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, fmt.Sprintf(script, testServiceName), startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 6, instructionsPlan.Size()) @@ -317,7 +317,7 @@ def run(plan): plan.add_service(name = service_name, config = config) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) expectedError := startosis_errors.NewInterpretationErrorWithCauseAndCustomMsg( errors.New("ServiceConfig: missing argument for image"), @@ -348,7 +348,7 @@ def run(plan): plan.add_service(name = service_name, config = config) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) expectedError := startosis_errors.NewInterpretationErrorWithCauseAndCustomMsg( startosis_errors.NewInterpretationError(`The following argument(s) could not be parsed or did not pass validation: {"transport_protocol":"Invalid argument value for 'transport_protocol': 'TCPK'. Valid values are TCP, SCTP, UDP"}`), []startosis_errors.CallFrame{ @@ -378,7 +378,7 @@ def run(plan): plan.add_service(name = service_name, config = config) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) expectedError := startosis_errors.NewInterpretationErrorWithCauseAndCustomMsg( startosis_errors.NewInterpretationError(`The following argument(s) could not be parsed or did not pass validation: {"number":"Value for 'number' was expected to be an integer between 1 and 65535, but it was 'starlark.String'"}`), []startosis_errors.CallFrame{ @@ -418,7 +418,7 @@ def run(plan): plan.print("Done!") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 8, instructionsPlan.Size()) @@ -446,7 +446,7 @@ load("` + barModulePath + `", "a") def run(plan): plan.print("Hello " + a) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) expectedError := startosis_errors.NewInterpretationErrorWithCustomMsg( []startosis_errors.CallFrame{ *startosis_errors.NewCallFrame("", startosis_errors.NewScriptPosition(startosis_constants.PackageIdPlaceholderForStandaloneScript, 2, 1)), @@ -469,7 +469,7 @@ my_module = import_module("` + barModulePath + `") def run(plan): plan.print("Hello " + my_module.a) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 1, instructionsPlan.Size()) // Only the print statement @@ -493,7 +493,7 @@ def run(plan): plan.print(module_doo.b) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 1, instructionsPlan.Size()) @@ -517,7 +517,7 @@ def run(plan): plan.print(module_doo.b) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) expectedError := startosis_errors.NewInterpretationErrorWithCustomMsg( []startosis_errors.CallFrame{ *startosis_errors.NewCallFrame("", startosis_errors.NewScriptPosition(moduleBarLoadsModuleDoo, 1, 27)), @@ -538,7 +538,7 @@ def run(plan): plan.print(my_module.b) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) errorMsg := `Evaluation error: An error occurred while loading the module '` + nonExistentModule + `' Caused by: Package '` + nonExistentModule + `' not found` @@ -561,7 +561,7 @@ def run(plan): ` // assert that first load fails - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.NotNil(suite.T(), interpretationError) require.Nil(suite.T(), instructionsPlan) @@ -570,7 +570,7 @@ def run(plan): expectedOutput := `Hello World! ` // assert that second load succeeds - _, instructionsPlan, interpretationError = suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError = suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 1, instructionsPlan.Size()) validateScriptOutputFromPrintInstructions(suite.T(), instructionsPlan, expectedOutput) @@ -597,7 +597,7 @@ def run(plan): plan.add_service(name = module_bar.service_name, config = module_bar.config) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 3, instructionsPlan.Size()) @@ -641,7 +641,7 @@ def run(plan): plan.print("Done!") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 8, instructionsPlan.Size()) @@ -670,7 +670,7 @@ def run(plan): plan.print("World!") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 1, instructionsPlan.Size()) @@ -708,7 +708,7 @@ Adding service example-datastore-server Starting Startosis script! ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, scriptA, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, scriptA, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 4, instructionsPlan.Size()) assertInstructionTypeAndPosition(suite.T(), instructionsPlan, 2, add_service.AddServiceBuiltinName, moduleBar, 12, 18) @@ -733,7 +733,7 @@ def run(plan): Adding service example-datastore-server ` - _, instructionsPlan, interpretationError = suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, scriptB, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError = suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, scriptB, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 3, instructionsPlan.Size()) assertInstructionTypeAndPosition(suite.T(), instructionsPlan, 2, add_service.AddServiceBuiltinName, startosis_constants.PackageIdPlaceholderForStandaloneScript, 14, 18) @@ -753,7 +753,7 @@ def run(plan): plan.print(file_contents) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 2, instructionsPlan.Size()) @@ -787,7 +787,7 @@ def run(plan): plan.print(artifact_name) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 3, instructionsPlan.Size()) @@ -826,7 +826,7 @@ def run(plan): plan.print(uuid) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 4, instructionsPlan.Size()) @@ -848,7 +848,7 @@ def run(plan): plan.print("The service example-datastore-server has been removed") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 3, instructionsPlan.Size()) @@ -866,7 +866,7 @@ func (suite *StartosisInterpreterTestSuite) TestStartosisInterpreter_NoPanicIfUp def run(plan): plan.upload_files("` + filePath + `") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.NotNil(suite.T(), interpretationError) require.Nil(suite.T(), instructionsPlan) } @@ -877,7 +877,7 @@ def run(plan): plan.print("Hello World!") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 1, instructionsPlan.Size()) @@ -892,7 +892,7 @@ def run(plan): plan.print("Hello World!") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"number": 4}`, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"number": 4}`, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.NotNil(suite.T(), interpretationError) require.Nil(suite.T(), instructionsPlan) } @@ -903,7 +903,7 @@ def run(plan, args): plan.print("My favorite number is {0}".format(args["number"])) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"number": 4}`, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"number": 4}`, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 1, instructionsPlan.Size()) @@ -921,7 +921,7 @@ def run(plan, args): plan.print("Sorry no args!") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 1, instructionsPlan.Size()) @@ -936,7 +936,7 @@ def run(plan, args, invalid_arg): plan.print("this wouldn't interpret so the text here doesnt matter") ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.NotNil(suite.T(), interpretationError) expectedError := "Evaluation error: function run missing 2 arguments (args, invalid_arg)" require.Contains(suite.T(), interpretationError.GetErrorMessage(), expectedError) @@ -950,7 +950,7 @@ def run(plan, a, b): ` missingArgumentCount := 1 missingArgument := "b" - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"a": "x"}`, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"a": "x"}`, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.NotNil(suite.T(), interpretationError) expectedError := fmt.Sprintf("Evaluation error: function run missing %d argument (%v)", missingArgumentCount, missingArgument) @@ -963,7 +963,7 @@ func (suite *StartosisInterpreterTestSuite) TestStartosisInterpreter_RunWithUnpa def run(plan, a, b=1): plan.print("My favorite number is {0}, but my favorite letter is {1}".format(b, a)) ` - _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"a": "x"}`, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, instructionsPlan, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, `{"a": "x"}`, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) require.Equal(suite.T(), 1, instructionsPlan.Size()) expectedOutput := "My favorite number is 1, but my favorite letter is x\n" @@ -976,7 +976,7 @@ def run(plan): print("this doesnt matter") ` - _, _, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, _, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.NotNil(suite.T(), interpretationError) require.Equal(suite.T(), fmt.Sprintf("Evaluation error: %v\n\tat [3:7]: run\n\tat [0:0]: print", print_builtin.UsePlanFromKurtosisInstructionError), interpretationError.GetErrorMessage()) } @@ -987,7 +987,7 @@ def run(plan): time.now() ` - _, _, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, _, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.NotNil(suite.T(), interpretationError) require.Equal(suite.T(), fmt.Sprintf("Evaluation error: %v\n\tat [3:10]: run\n\tat [0:0]: now", time_now_builtin.UseRunPythonInsteadOfTimeNowError), interpretationError.GetErrorMessage()) } @@ -998,7 +998,7 @@ def run(plan): time.parse_duration("5s") ` - _, _, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, emptyEnclaveComponents, emptyInstructionsPlanMask) + _, _, interpretationError := suite.interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, useDefaultMainFunctionName, noPackageReplaceOptions, startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, script, startosis_constants.EmptyInputArgs, defaultNonBlockingMode, emptyEnclaveComponents, emptyInstructionsPlanMask) require.Nil(suite.T(), interpretationError) } diff --git a/core/server/api_container/server/startosis_engine/startosis_runner.go b/core/server/api_container/server/startosis_engine/startosis_runner.go index ab4c8210b0..dd13197c73 100644 --- a/core/server/api_container/server/startosis_engine/startosis_runner.go +++ b/core/server/api_container/server/startosis_engine/startosis_runner.go @@ -56,6 +56,7 @@ func (runner *StartosisRunner) Run( serializedStartosis string, serializedParams string, imageDownloadMode image_download_mode.ImageDownloadMode, + nonBlockingMode bool, experimentalFeatures []kurtosis_core_rpc_api_bindings.KurtosisFeatureFlag, ) <-chan *kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine { runner.mutex.Lock() @@ -76,7 +77,6 @@ func (runner *StartosisRunner) Run( } starlarkRunResponseLines <- binding_constructors.NewStarlarkRunResponseLineFromInfoMsg("Made with Kurtosis - https://kurtosis.com") - close(starlarkRunResponseLines) }() @@ -111,6 +111,7 @@ func (runner *StartosisRunner) Run( relativePathToMainFile, serializedStartosis, serializedParams, + nonBlockingMode, enclave_structure.NewEnclaveComponents(), resolver.NewInstructionsPlanMask(0), ) @@ -123,6 +124,7 @@ func (runner *StartosisRunner) Run( relativePathToMainFile, serializedStartosis, serializedParams, + nonBlockingMode, runner.startosisExecutor.enclavePlan, ) } diff --git a/engine/server/engine/server/enclave_rest_api_handler.go b/engine/server/engine/server/enclave_rest_api_handler.go index 1987c45873..14f89c5031 100644 --- a/engine/server/engine/server/enclave_rest_api_handler.go +++ b/engine/server/engine/server/enclave_rest_api_handler.go @@ -601,6 +601,7 @@ func (manager *enclaveRuntime) PostEnclavesEnclaveIdentifierStarlarkPackagesPack // RunStarlarkPackage. If the package is remote and must be cloned within the APIC, use the standalone boolean flag // clone_package below StarlarkPackageContent: nil, + NonBlockingMode: request.Body.NonBlockingMode, } ctxWithCancel, cancelCtxFunc := context.WithCancel(context.Background()) @@ -673,6 +674,7 @@ func (manager *enclaveRuntime) PostEnclavesEnclaveIdentifierStarlarkScripts(ctx CloudInstanceId: request.Body.CloudInstanceId, CloudUserId: request.Body.CloudUserId, ImageDownloadMode: utils.MapPointer(request.Body.ImageDownloadMode, to_grpc.ToGrpcImageDownloadMode), + NonBlockingMode: request.Body.NonBlockingMode, } ctxWithCancel, cancelCtxFunc := context.WithCancel(context.Background()) diff --git a/package-lock.json b/package-lock.json index 12baf66b21..66deac5caa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,10 +5,16 @@ "packages": { "": { "dependencies": { +<<<<<<< HEAD + "@connectrpc/protoc-gen-connect-es": "^1.3.0", + "openapi-typescript": "7.0.0-next.5", + "protoc-gen-ts": "^0.8.7" +======= "@bufbuild/buf": "^1.29.0", "@bufbuild/protobuf": "^1.7.2", "@bufbuild/protoc-gen-es": "^1.7.2", "openapi-typescript": "7.0.0-next.5" +>>>>>>> main }, "devDependencies": { "@tailwindcss/typography": "^0.5.9" @@ -27,6 +33,8 @@ "url": "https://github.com/sponsors/sindresorhus" } }, +<<<<<<< HEAD +======= "node_modules/@bufbuild/buf": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.29.0.tgz", @@ -139,11 +147,14 @@ "node": ">=12" } }, +>>>>>>> main "node_modules/@bufbuild/protobuf": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.7.2.tgz", "integrity": "sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ==" }, +<<<<<<< HEAD +======= "node_modules/@bufbuild/protoc-gen-es": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.7.2.tgz", @@ -167,6 +178,7 @@ } } }, +>>>>>>> main "node_modules/@bufbuild/protoplugin": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.7.2.tgz", @@ -177,6 +189,36 @@ "typescript": "4.5.2" } }, +<<<<<<< HEAD + "node_modules/@connectrpc/protoc-gen-connect-es": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@connectrpc/protoc-gen-connect-es/-/protoc-gen-connect-es-1.3.0.tgz", + "integrity": "sha512-UbQN48c0zafo5EFSsh3POIJP6ofYiAgKE1aFOZ2Er4W3flUYihydZdM6TQauPkn7jDj4w9jjLSTTZ9//ecUbPA==", + "dependencies": { + "@bufbuild/protobuf": "^1.6.0", + "@bufbuild/protoplugin": "^1.6.0" + }, + "bin": { + "protoc-gen-connect-es": "bin/protoc-gen-connect-es" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@bufbuild/protoc-gen-es": "^1.6.0", + "@connectrpc/connect": "1.3.0" + }, + "peerDependenciesMeta": { + "@bufbuild/protoc-gen-es": { + "optional": true + }, + "@connectrpc/connect": { + "optional": true + } + } + }, +======= +>>>>>>> main "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -284,9 +326,15 @@ } }, "node_modules/@redocly/openapi-core": { +<<<<<<< HEAD + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.9.0.tgz", + "integrity": "sha512-n8Uye07wx5ca59HPIxmfPYFnYW/L60i2uaBnOgmDXZIxA61xVJ48YTRSaSLUluoidfy9XwNJv0XX7Mz9uNnF2w==", +======= "version": "1.8.2", "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.8.2.tgz", "integrity": "sha512-VjUz3wrqcDbO1HfEB0AUzh6Y7T1jNJR4Jmgfs0ipuoipLjU5bDsdfKJGSSz2u0WpfmqklPsd11ynkgL5Y+MlCg==", +>>>>>>> main "dependencies": { "@redocly/ajv": "^8.11.0", "colorette": "^1.2.0", @@ -1200,6 +1248,21 @@ "dev": true, "peer": true }, +<<<<<<< HEAD + "node_modules/protoc-gen-ts": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/protoc-gen-ts/-/protoc-gen-ts-0.8.7.tgz", + "integrity": "sha512-jr4VJey2J9LVYCV7EVyVe53g1VMw28cCmYJhBe5e3YX5wiyiDwgxWxeDf9oTqAe4P1bN/YGAkW2jhlH8LohwiQ==", + "bin": { + "protoc-gen-ts": "protoc-gen-ts.js" + }, + "funding": { + "type": "individual", + "url": "https://www.buymeacoffee.com/thesayyn" + } + }, +======= +>>>>>>> main "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1549,6 +1612,8 @@ "dev": true, "peer": true }, +<<<<<<< HEAD +======= "@bufbuild/buf": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.29.0.tgz", @@ -1598,11 +1663,14 @@ "integrity": "sha512-wRk6co+nqHqEq4iLolXgej0jUVlWlTtGHjKaq54lTbKZrwxrBgql6qS06abgNPRASX0++XT9m3QRZ97qEIC/HQ==", "optional": true }, +>>>>>>> main "@bufbuild/protobuf": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.7.2.tgz", "integrity": "sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ==" }, +<<<<<<< HEAD +======= "@bufbuild/protoc-gen-es": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.7.2.tgz", @@ -1612,6 +1680,7 @@ "@bufbuild/protoplugin": "1.7.2" } }, +>>>>>>> main "@bufbuild/protoplugin": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.7.2.tgz", @@ -1622,6 +1691,18 @@ "typescript": "4.5.2" } }, +<<<<<<< HEAD + "@connectrpc/protoc-gen-connect-es": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@connectrpc/protoc-gen-connect-es/-/protoc-gen-connect-es-1.3.0.tgz", + "integrity": "sha512-UbQN48c0zafo5EFSsh3POIJP6ofYiAgKE1aFOZ2Er4W3flUYihydZdM6TQauPkn7jDj4w9jjLSTTZ9//ecUbPA==", + "requires": { + "@bufbuild/protobuf": "^1.6.0", + "@bufbuild/protoplugin": "^1.6.0" + } + }, +======= +>>>>>>> main "@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -1707,9 +1788,15 @@ } }, "@redocly/openapi-core": { +<<<<<<< HEAD + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.9.0.tgz", + "integrity": "sha512-n8Uye07wx5ca59HPIxmfPYFnYW/L60i2uaBnOgmDXZIxA61xVJ48YTRSaSLUluoidfy9XwNJv0XX7Mz9uNnF2w==", +======= "version": "1.8.2", "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.8.2.tgz", "integrity": "sha512-VjUz3wrqcDbO1HfEB0AUzh6Y7T1jNJR4Jmgfs0ipuoipLjU5bDsdfKJGSSz2u0WpfmqklPsd11ynkgL5Y+MlCg==", +>>>>>>> main "requires": { "@redocly/ajv": "^8.11.0", "colorette": "^1.2.0", @@ -2385,6 +2472,14 @@ "dev": true, "peer": true }, +<<<<<<< HEAD + "protoc-gen-ts": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/protoc-gen-ts/-/protoc-gen-ts-0.8.7.tgz", + "integrity": "sha512-jr4VJey2J9LVYCV7EVyVe53g1VMw28cCmYJhBe5e3YX5wiyiDwgxWxeDf9oTqAe4P1bN/YGAkW2jhlH8LohwiQ==" + }, +======= +>>>>>>> main "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/yarn.lock b/yarn.lock index 8275503de3..93f436f7ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,6 +3,91 @@ "@alloc/quick-lru@^5.2.0": +<<<<<<< HEAD + version "5.2.0" + resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@bufbuild/protobuf@^1.6.0", "@bufbuild/protobuf@1.7.2": + version "1.7.2" + resolved "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.7.2.tgz" + integrity sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ== + +"@bufbuild/protoplugin@^1.6.0": + version "1.7.2" + resolved "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.7.2.tgz" + integrity sha512-N3QtO8XWD4F4alMtASWtxBw6BWXp4aLz7rPBXH4KTULdjpUHnq46g15TsrG0/8szZw6pIklTO3lFe14dl6ZYdA== + dependencies: + "@bufbuild/protobuf" "1.7.2" + "@typescript/vfs" "^1.4.0" + typescript "4.5.2" + +"@connectrpc/protoc-gen-connect-es@^1.3.0": + version "1.3.0" + resolved "https://registry.npmjs.org/@connectrpc/protoc-gen-connect-es/-/protoc-gen-connect-es-1.3.0.tgz" + integrity sha512-UbQN48c0zafo5EFSsh3POIJP6ofYiAgKE1aFOZ2Er4W3flUYihydZdM6TQauPkn7jDj4w9jjLSTTZ9//ecUbPA== + dependencies: + "@bufbuild/protobuf" "^1.6.0" + "@bufbuild/protoplugin" "^1.6.0" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.19" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@redocly/ajv@^8.11.0": + version "8.11.0" + resolved "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz" + integrity sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw== +======= "integrity" "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" "resolved" "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz" "version" "5.2.0" @@ -33,6 +118,7 @@ "integrity" "sha512-yiRk/T+YGmpSVvIkybCjPt+QyM/pLWMO+MAiz6auvCsiAgfXfc5nFFosD4yBYXID55M6eIkgBcity1AoJ6I30A==" "resolved" "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.7.2.tgz" "version" "1.7.2" +>>>>>>> main dependencies: "@bufbuild/protobuf" "^1.7.2" "@bufbuild/protoplugin" "1.7.2" @@ -110,6 +196,26 @@ "uri-js" "^4.2.2" "@redocly/openapi-core@^1.4.1": +<<<<<<< HEAD + version "1.9.0" + resolved "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.9.0.tgz" + integrity sha512-n8Uye07wx5ca59HPIxmfPYFnYW/L60i2uaBnOgmDXZIxA61xVJ48YTRSaSLUluoidfy9XwNJv0XX7Mz9uNnF2w== + dependencies: + "@redocly/ajv" "^8.11.0" + colorette "^1.2.0" + js-levenshtein "^1.1.6" + js-yaml "^4.1.0" + lodash.isequal "^4.5.0" + minimatch "^5.0.1" + node-fetch "^2.6.1" + pluralize "^8.0.0" + yaml-ast-parser "0.0.43" + +"@tailwindcss/typography@^0.5.9": + version "0.5.9" + resolved "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz" + integrity sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg== +======= "integrity" "sha512-VjUz3wrqcDbO1HfEB0AUzh6Y7T1jNJR4Jmgfs0ipuoipLjU5bDsdfKJGSSz2u0WpfmqklPsd11ynkgL5Y+MlCg==" "resolved" "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.8.2.tgz" "version" "1.8.2" @@ -128,6 +234,7 @@ "integrity" "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==" "resolved" "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz" "version" "0.5.9" +>>>>>>> main dependencies: "lodash.castarray" "^4.4.0" "lodash.isplainobject" "^4.0.6" @@ -135,12 +242,280 @@ "postcss-selector-parser" "6.0.10" "@typescript/vfs@^1.4.0": +<<<<<<< HEAD + version "1.5.0" + resolved "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz" + integrity sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg== + dependencies: + debug "^4.1.1" + +ansi-colors@^4.1.3: + version "4.1.3" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== +======= "integrity" "sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==" "resolved" "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz" "version" "1.5.0" +>>>>>>> main dependencies: "debug" "^4.1.1" +<<<<<<< HEAD +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +colorette@^1.2.0: + version "1.4.0" + resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +debug@^4.1.1: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.12: + version "3.3.1" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.6: + version "7.1.6" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +jiti@^1.18.2: + version "1.19.3" + resolved "https://registry.npmjs.org/jiti/-/jiti-1.19.3.tgz" + integrity sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w== + +js-levenshtein@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== +======= "ansi-colors@^4.1.3": "integrity" "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz" @@ -155,10 +530,72 @@ "integrity" "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==" "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" "version" "3.1.3" +>>>>>>> main dependencies: "normalize-path" "^3.0.0" "picomatch" "^2.0.4" +<<<<<<< HEAD +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +lilconfig@^2.0.5, lilconfig@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +lodash.castarray@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz" + integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== +======= "arg@^5.0.2": "integrity" "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" "resolved" "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" @@ -183,17 +620,72 @@ "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" "version" "1.1.11" +>>>>>>> main dependencies: "balanced-match" "^1.0.0" "concat-map" "0.0.1" +<<<<<<< HEAD +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + +node-fetch@^2.6.1: + version "2.7.0" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== +======= "brace-expansion@^2.0.1": "integrity" "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==" "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" "version" "2.0.1" +>>>>>>> main dependencies: "balanced-match" "^1.0.0" +<<<<<<< HEAD +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +openapi-typescript@7.0.0-next.5: + version "7.0.0-next.5" + resolved "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.0.0-next.5.tgz" + integrity sha512-zqEDw/FZkT0ndOCd8EybkDVwEYgaOh+ryWm6OCON70DmY9YqUnNSIVyRFVjN8hesa0bxOs9QOMzXAasczNdHbQ== +======= "braces@^3.0.2", "braces@~3.0.2": "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" @@ -518,6 +1010,7 @@ "integrity" "sha512-zqEDw/FZkT0ndOCd8EybkDVwEYgaOh+ryWm6OCON70DmY9YqUnNSIVyRFVjN8hesa0bxOs9QOMzXAasczNdHbQ==" "resolved" "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.0.0-next.5.tgz" "version" "7.0.0-next.5" +>>>>>>> main dependencies: "@redocly/openapi-core" "^1.4.1" "ansi-colors" "^4.1.3" @@ -525,6 +1018,86 @@ "typescript" "^5.3.2" "yargs-parser" "^21.1.1" +<<<<<<< HEAD +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz" + integrity sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA== + dependencies: + lilconfig "^2.0.5" + yaml "^2.1.1" + +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz" + integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== + dependencies: + postcss-selector-parser "^6.0.11" + +postcss-selector-parser@^6.0.11: + version "6.0.13" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-selector-parser@6.0.10: + version "6.0.10" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== +======= "path-is-absolute@^1.0.0": "integrity" "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" @@ -564,26 +1137,238 @@ "integrity" "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==" "resolved" "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz" "version" "15.1.0" +>>>>>>> main dependencies: "postcss-value-parser" "^4.0.0" "read-cache" "^1.0.0" "resolve" "^1.1.7" +<<<<<<< HEAD +postcss-value-parser@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.0.0, postcss@^8.2.14, postcss@^8.4.21, postcss@^8.4.23, postcss@>=8.0.9: + version "8.4.31" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +protoc-gen-ts@^0.8.7: + version "0.8.7" + resolved "https://registry.npmjs.org/protoc-gen-ts/-/protoc-gen-ts-0.8.7.tgz" + integrity sha512-jr4VJey2J9LVYCV7EVyVe53g1VMw28cCmYJhBe5e3YX5wiyiDwgxWxeDf9oTqAe4P1bN/YGAkW2jhlH8LohwiQ== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve@^1.1.7, resolve@^1.22.2: + version "1.22.4" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +sucrase@^3.32.0: + version "3.34.0" + resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz" + integrity sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "7.1.6" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + +supports-color@^9.4.0: + version "9.4.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +"tailwindcss@>=3.0.0 || insiders": + version "3.3.3" + resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz" + integrity sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.5.3" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.2.12" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.18.2" + lilconfig "^2.1.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" + postcss-selector-parser "^6.0.11" + resolve "^1.22.2" + sucrase "^3.32.0" + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +typescript@^5.3.2: + version "5.3.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + +typescript@4.5.2: + version "4.5.2" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz" + integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== +======= "postcss-js@^4.0.1": "integrity" "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==" "resolved" "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz" "version" "4.0.1" +>>>>>>> main dependencies: "camelcase-css" "^2.0.1" +<<<<<<< HEAD +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== +======= "postcss-load-config@^4.0.1": "integrity" "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==" "resolved" "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz" "version" "4.0.1" +>>>>>>> main dependencies: "lilconfig" "^2.0.5" "yaml" "^2.1.1" +<<<<<<< HEAD +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yaml-ast-parser@0.0.43: + version "0.0.43" + resolved "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz" + integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== + +yaml@^2.1.1: + version "2.3.2" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz" + integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +======= "postcss-nested@^6.0.1": "integrity" "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==" "resolved" "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz" @@ -812,3 +1597,4 @@ "integrity" "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" "version" "21.1.1" +>>>>>>> main From 26ad5716548edec84e5276ddfe972203c7d5838d Mon Sep 17 00:00:00 2001 From: Ben Gazzard Date: Thu, 15 Feb 2024 18:06:48 +0000 Subject: [PATCH 069/102] feat: emui builder python nodes (#2170) ## Description: This PR primarily adds support for `run_python` nodes in the enclave builder graph. It removes the `exec` node that was added yesterday, as getting this node correct in the topological sort was more complex than just merging it's behaviour with the service node (so that's what I've done). A couple of small changes are also include: * memo behaviour on node selection was wrong, * When zooming out show a simplified version of nodes * Update icons and add some node type text to make it clearer what's what. ## Is this change user facing? YES (experimental flag) ## References (if applicable): * discussed on slack --- .../components/form/SelectArgumentInput.tsx | 4 +- .../enclaveBuilder/KurtosisArtifactNode.tsx | 21 +- .../enclaveBuilder/KurtosisExecNode.tsx | 105 ------ .../modals/enclaveBuilder/KurtosisNode.tsx | 318 +++++++++++------- .../enclaveBuilder/KurtosisPythonNode.tsx | 168 +++++++++ .../enclaveBuilder/KurtosisServiceNode.tsx | 67 +++- .../enclaveBuilder/KurtosisShellNode.tsx | 14 +- .../modals/enclaveBuilder/Visualiser.css | 7 + .../modals/enclaveBuilder/Visualiser.tsx | 44 ++- .../input/MentionStringArgumentInput.tsx | 4 +- .../input/MountArtifactFileInput.tsx | 2 +- .../components/modals/enclaveBuilder/types.ts | 31 +- .../components/modals/enclaveBuilder/utils.ts | 121 +++++-- 13 files changed, 590 insertions(+), 316 deletions(-) delete mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisExecNode.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisPythonNode.tsx create mode 100644 enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.css diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/form/SelectArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/SelectArgumentInput.tsx index 7cbe07eb40..b5ff1d83dc 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/form/SelectArgumentInput.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/form/SelectArgumentInput.tsx @@ -23,7 +23,9 @@ export const SelectArgumentInput = ({ return ( ); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx index 84fb5d78b1..26b5a0de79 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisArtifactNode.tsx @@ -1,4 +1,3 @@ -import { isDefined } from "kurtosis-ui-components"; import { memo } from "react"; import { NodeProps } from "reactflow"; import { KurtosisFormControl } from "../../form/KurtosisFormControl"; @@ -7,27 +6,11 @@ import { FileTreeArgumentInput } from "./input/FileTreeArgumentInput"; import { validateName } from "./input/validators"; import { KurtosisNode } from "./KurtosisNode"; import { KurtosisArtifactNodeData } from "./types"; -import { useVariableContext } from "./VariableContextProvider"; export const KurtosisArtifactNode = memo( ({ id, selected }: NodeProps) => { - const { data } = useVariableContext(); - const nodeData = data[id] as KurtosisArtifactNodeData; - - if (!isDefined(nodeData)) { - // Node has probably been deleted. - return null; - } - return ( - + name={"artifactName"} label={"Artifact Name"} isRequired> @@ -37,5 +20,5 @@ export const KurtosisArtifactNode = memo( ); }, - (oldProps, newProps) => oldProps.id !== newProps.id || oldProps.selected !== newProps.selected, + (oldProps, newProps) => oldProps.id === newProps.id && oldProps.selected === newProps.selected, ); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisExecNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisExecNode.tsx deleted file mode 100644 index d24ff1b9f3..0000000000 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisExecNode.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; -import { isDefined } from "kurtosis-ui-components"; -import { memo, useMemo } from "react"; -import { NodeProps } from "reactflow"; -import { IntegerArgumentInput } from "../../form/IntegerArgumentInput"; -import { KurtosisFormControl } from "../../form/KurtosisFormControl"; -import { ListArgumentInput } from "../../form/ListArgumentInput"; -import { SelectArgumentInput, SelectOption } from "../../form/SelectArgumentInput"; -import { StringArgumentInput } from "../../form/StringArgumentInput"; -import { KurtosisFormInputProps } from "../../form/types"; -import { MentionStringArgumentInput } from "./input/MentionStringArgumentInput"; -import { validateName } from "./input/validators"; -import { KurtosisNode } from "./KurtosisNode"; -import { KurtosisExecNodeData, KurtosisServiceNodeData } from "./types"; -import { useVariableContext } from "./VariableContextProvider"; - -export const KurtosisExecNode = memo( - ({ id, selected }: NodeProps) => { - const { data, variables } = useVariableContext(); - const nodeData = data[id] as KurtosisExecNodeData; - - const serviceVariableOptions = useMemo((): SelectOption[] => { - return variables - .filter((variable) => variable.id.match(/^service\.[^.]+\.name+$/)) - .map((variable) => ({ - display: variable.displayName.replace(/service\.(.*)\.name/, "$1"), - value: `{{${variable.id}}}`, - })); - }, [variables]); - - if (!isDefined(nodeData)) { - // Node has probably been deleted. - return null; - } - - return ( - - name={"execName"} label={"Exec Name"} isRequired> - - - - - Config - Advanced - - - - {" "} - - name={"serviceName"} - label={"Service"} - helperText={"Choose which service to run this command in."} - isRequired - > - - options={serviceVariableOptions} - isRequired - size={"sm"} - placeholder={"Select a Service"} - name={`serviceName`} - /> - - name={"command"} label={"Command"} isRequired> - - - - - - name={"acceptableCodes"} - label={"Acceptable Exit Codes"} - isRequired - > - - FieldComponent={AcceptableCodeInput} - size={"sm"} - name={"acceptableCodes"} - createNewValue={() => ({ value: 0 })} - isRequired - /> - - - - - - ); - }, - (oldProps, newProps) => oldProps.id !== newProps.id || oldProps.selected !== newProps.selected, -); - -const AcceptableCodeInput = (props: KurtosisFormInputProps) => { - return ( - - {...props} - size={"sm"} - name={`${props.name as `acceptableCodes.${number}`}.value`} - /> - ); -}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisNode.tsx index 1a41c7334b..0ace2c38e9 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisNode.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisNode.tsx @@ -1,141 +1,231 @@ -import { Flex, IconButton, Text, useToken } from "@chakra-ui/react"; +import { Flex, Icon, IconButton, Text, useToken } from "@chakra-ui/react"; +import { isDefined } from "kurtosis-ui-components"; import { debounce } from "lodash"; -import { memo, PropsWithChildren, useEffect, useMemo } from "react"; +import { FC, memo, PropsWithChildren, useEffect, useMemo } from "react"; import { DefaultValues, FormProvider, useForm } from "react-hook-form"; -import { FiTrash } from "react-icons/fi"; +import { FiCpu, FiFile, FiTerminal, FiTrash } from "react-icons/fi"; import { RxCornerBottomRight } from "react-icons/rx"; -import { Handle, NodeResizeControl, Position, useReactFlow } from "reactflow"; +import { Handle, NodeResizeControl, Position, useReactFlow, useViewport } from "reactflow"; import { KurtosisNodeData } from "./types"; +import { getNodeName } from "./utils"; import { useVariableContext } from "./VariableContextProvider"; +const colors: Record = { + service: "blue.900", + artifact: "yellow.900", + shell: "red.900", + python: "red.900", +}; + +export const nodeIcons: Record = { + service: FiCpu, + artifact: FiFile, + shell: FiTerminal, + python: FiTerminal, +}; + +const nodeTypeReadable: Record = { + service: "Service", + artifact: "Files", + shell: "Shell execution task", + python: "Python execution task", +}; + type KurtosisNodeProps = PropsWithChildren<{ id: string; - name: string; selected: boolean; minWidth: number; maxWidth: number; - color: string; }>; export const KurtosisNode = memo( ({ id, - name, + selected, minWidth, maxWidth, children, - color, }: KurtosisNodeProps) => { - const chakraColor = useToken("colors", color); - const { data, updateData, removeData } = useVariableContext(); - const formMethods = useForm({ - defaultValues: (data[id] as DefaultValues) || {}, - mode: "onBlur", - shouldFocusError: false, - }); - - const { deleteElements, zoomOut, zoomIn } = useReactFlow(); - - const handleDeleteNode = (e: React.MouseEvent) => { - e.stopPropagation(); - e.preventDefault(); - deleteElements({ nodes: [{ id }] }); - removeData(id); - }; - - const handleChange = useMemo( - () => - debounce(async () => { - const isValid = await formMethods.trigger(); - updateData(id, { ...formMethods.getValues(), isValid }); - }, 500), - [updateData, formMethods, id], - ); + const { data } = useVariableContext(); + const nodeData = data[id] as DataType; - useEffect(() => { - const watcher = formMethods.watch(handleChange); - return () => watcher.unsubscribe(); - }, [formMethods, handleChange]); - - const handleScroll = (e: React.WheelEvent) => { - if (e.currentTarget.scrollTop === 0 && e.deltaY < 0) { - zoomIn(); - } - if ( - Math.abs(e.currentTarget.scrollHeight - e.currentTarget.clientHeight - e.currentTarget.scrollTop) <= 1 && - e.deltaY > 0 - ) { - zoomOut(); - } - }; + if (!isDefined(nodeData)) { + return null; + } return ( - - - - - - - - - - {name || Unnamed} - } - colorScheme={"red"} - variant={"ghost"} - size={"sm"} - onClick={handleDeleteNode} - /> - - - {children} - - - + + id={id} + selected={selected} + minWidth={minWidth} + maxWidth={maxWidth} + nodeData={nodeData} + > + {children} + ); }, ); + +type KurtosisNodeImplProps = KurtosisNodeProps & { nodeData: DataType }; +const KurtosisNodeImpl = ({ + id, + nodeData, + selected, + minWidth, + maxWidth, + children, +}: KurtosisNodeImplProps) => { + const { updateData, removeData } = useVariableContext(); + const color = colors[nodeData.type]; + const chakraColor = useToken("colors", color); + const name = useMemo(() => getNodeName(nodeData), [nodeData]); + const formMethods = useForm({ + defaultValues: nodeData as DefaultValues, + mode: "onBlur", + shouldFocusError: false, + }); + + const { deleteElements } = useReactFlow(); + + const handleDeleteNode = (e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + deleteElements({ nodes: [{ id }] }); + removeData(id); + }; + + const handleChange = useMemo( + () => + debounce(async () => { + const isValid = await formMethods.trigger(); + updateData(id, { ...formMethods.getValues(), isValid }); + }, 500), + [updateData, formMethods, id], + ); + + useEffect(() => { + const watcher = formMethods.watch(handleChange); + return () => watcher.unsubscribe(); + }, [formMethods, handleChange]); + + if (!isDefined(nodeData)) { + return null; + } + + return ( + + + + + + + + + {children} + + + + ); +}; + +type ZoomAwareNodeContentProps = PropsWithChildren<{ + name: string; + type: KurtosisNodeData["type"]; + onDelete: (e: React.MouseEvent) => void; +}>; + +const ZoomAwareNodeContent = ({ name, type, onDelete, children }: ZoomAwareNodeContentProps) => { + const viewport = useViewport(); + const { zoomOut, zoomIn } = useReactFlow(); + + const handleScroll = (e: React.WheelEvent) => { + if (e.currentTarget.scrollTop === 0 && e.deltaY < 0) { + zoomIn(); + } + if ( + Math.abs(e.currentTarget.scrollHeight - e.currentTarget.clientHeight - e.currentTarget.scrollTop) <= 1 && + e.deltaY > 0 + ) { + zoomOut(); + } + }; + + if (viewport.zoom < 0.4) { + return ( + + + + {name || Unnamed} + + + ); + } + + return ( + <> + + + + {name || Unnamed} + + {nodeTypeReadable[type]} + + + } + colorScheme={"red"} + variant={"ghost"} + size={"sm"} + onClick={onDelete} + /> + + + {children} + + + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisPythonNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisPythonNode.tsx new file mode 100644 index 0000000000..3d3c0c01e8 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisPythonNode.tsx @@ -0,0 +1,168 @@ +import { Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; +import { isDefined } from "kurtosis-ui-components"; +import { memo } from "react"; +import { NodeProps } from "reactflow"; +import { BooleanArgumentInput } from "../../form/BooleanArgumentInput"; +import { CodeEditorInput } from "../../form/CodeEditorInput"; +import { KurtosisFormControl } from "../../form/KurtosisFormControl"; +import { ListArgumentInput } from "../../form/ListArgumentInput"; +import { StringArgumentInput } from "../../form/StringArgumentInput"; +import { KurtosisFormInputProps } from "../../form/types"; +import { MentionStringArgumentInput } from "./input/MentionStringArgumentInput"; +import { MountArtifactFileInput } from "./input/MountArtifactFileInput"; +import { validateDockerLocator, validateDurationString, validateName } from "./input/validators"; +import { KurtosisNode } from "./KurtosisNode"; +import { KurtosisFileMount, KurtosisPythonNodeData } from "./types"; +import { useVariableContext } from "./VariableContextProvider"; + +export const KurtosisPythonNode = memo( + ({ id, selected }: NodeProps) => { + const { data } = useVariableContext(); + const nodeData = data[id] as KurtosisPythonNodeData; + + if (!isDefined(nodeData)) { + return null; + } + + return ( + + + name={"pythonName"} label={"Python Name"} isRequired> + + + name={"image"} label={"Container Image"}> + + + + + + Code + Packages + Arguments + Files + Advanced + + + + + name={"command"} label={"Code to run"} isRequired> + + + + + + name={"packages"} + label={"Packages"} + isRequired + helperText={"Names of packages that need to be installed prior to running this code"} + > + + FieldComponent={PackageInput} + createNewValue={() => ({ packageName: "" })} + name={"packages"} + size={"sm"} + isRequired + validate={validateName} + /> + + + + + name={"args"} + label={"Arguments"} + helperText={"Arguments to be passed to the Python script"} + > + + name={"args"} + FieldComponent={PythonArgInput} + createNewValue={() => ({ arg: "" })} + isRequired + /> + + + + + name={"files"} + label={"Input Files"} + helperText={"Choose where to mount artifacts on this execution tasks filesystem"} + > + ({ + mountPoint: "", + artifactName: "", + })} + /> + + + name={"store"} + label={"Output File/Directory"} + helperText={ + "Choose which files to expose from this execution task. You can use either an absolute path, a directory, or a glob." + } + > + + name={"store"} + placeholder={"/some/output/location"} + /> + + + + + + name={"wait_enabled"} + label={"Wait enabled"} + isRequired + helperText={"Whether kurtosis should wait a preset time for this step to complete."} + > + name={"wait_enabled"} /> + + + name={"wait"} + label={"Wait"} + isDisabled={nodeData.wait_enabled === "false"} + helperText={"Whether kurtosis should wait a preset time for this step to complete."} + > + + name={"wait"} + isDisabled={nodeData.wait_enabled === "false"} + size={"sm"} + placeholder={"180s"} + validate={nodeData.wait_enabled === "false" ? undefined : validateDurationString} + /> + + + + + + + ); + }, + (oldProps, newProps) => oldProps.id === newProps.id && oldProps.selected === newProps.selected, +); + +const PackageInput = (props: KurtosisFormInputProps) => { + return ( + + {...props} + size={"sm"} + name={`${props.name as `packages.${number}`}.packageName`} + /> + ); +}; + +const PythonArgInput = (props: KurtosisFormInputProps) => { + return ( + + {...props} + width={"400px"} + name={`${props.name as `args.${number}`}.arg`} + /> + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx index 5ddb6abb4b..a2bb357c29 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisServiceNode.tsx @@ -1,10 +1,13 @@ import { Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; import { memo } from "react"; import { NodeProps } from "reactflow"; +import { BooleanArgumentInput } from "../../form/BooleanArgumentInput"; import { DictArgumentInput } from "../../form/DictArgumentInput"; +import { IntegerArgumentInput } from "../../form/IntegerArgumentInput"; import { KurtosisFormControl } from "../../form/KurtosisFormControl"; import { ListArgumentInput } from "../../form/ListArgumentInput"; import { StringArgumentInput } from "../../form/StringArgumentInput"; +import { KurtosisFormInputProps } from "../../form/types"; import { MentionStringArgumentInput } from "./input/MentionStringArgumentInput"; import { MountArtifactFileInput } from "./input/MountArtifactFileInput"; import { PortConfigurationField } from "./input/PortConfigurationInput"; @@ -16,16 +19,10 @@ import { useVariableContext } from "./VariableContextProvider"; export const KurtosisServiceNode = memo( ({ id, selected }: NodeProps) => { const { data } = useVariableContext(); + const nodeData = data[id] as KurtosisServiceNodeData; return ( - + name={"serviceName"} label={"Service Name"} isRequired> @@ -39,6 +36,7 @@ export const KurtosisServiceNode = memo( Environment Ports Files + Exec @@ -81,10 +79,61 @@ export const KurtosisServiceNode = memo( /> + + + + name={"execStepEnabled"} + label={"Exec step enabled"} + isRequired + helperText={"Whether kurtosis should execute a command in this service once the service is ready."} + > + name={"execStepEnabled"} /> + + + name={"execStepCommand"} + label={"Command"} + isRequired={nodeData.execStepEnabled === "true"} + isDisabled={nodeData.execStepEnabled === "false"} + > + + + + name={"execStepAcceptableCodes"} + label={"Acceptable Exit Codes"} + isDisabled={nodeData.execStepEnabled === "false"} + helperText={ + "If the executed command returns a code not on this list starlark will fail. Defaults to [0]" + } + > + + FieldComponent={AcceptableCodeInput} + size={"sm"} + name={"execStepAcceptableCodes"} + createNewValue={() => ({ value: 0 })} + disabled={nodeData.execStepEnabled === "false"} + /> + + + ); }, - (oldProps, newProps) => oldProps.id !== newProps.id || oldProps.selected !== newProps.selected, + (oldProps, newProps) => oldProps.id === newProps.id && oldProps.selected === newProps.selected, ); + +const AcceptableCodeInput = (props: KurtosisFormInputProps) => { + return ( + + {...props} + size={"sm"} + name={`${props.name as `execStepAcceptableCodes.${number}`}.value`} + /> + ); +}; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisShellNode.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisShellNode.tsx index f354bb5a6e..d77844add0 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisShellNode.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/KurtosisShellNode.tsx @@ -21,19 +21,11 @@ export const KurtosisShellNode = memo( const nodeData = data[id] as KurtosisShellNodeData; if (!isDefined(nodeData)) { - // Node has probably been deleted. return null; } return ( - + name={"shellName"} label={"Shell Name"} isRequired> @@ -58,7 +50,7 @@ export const KurtosisShellNode = memo( name={"command"} label={"Script to run"} isRequired> - + @@ -131,5 +123,5 @@ export const KurtosisShellNode = memo( ); }, - (oldProps, newProps) => oldProps.id !== newProps.id || oldProps.selected !== newProps.selected, + (oldProps, newProps) => oldProps.id === newProps.id && oldProps.selected === newProps.selected, ); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.css b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.css new file mode 100644 index 0000000000..9fc3f98513 --- /dev/null +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.css @@ -0,0 +1,7 @@ +.react-flow__handle-right { + right: 50%; +} + +.react-flow__handle-left { + left: 50%; +} diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.tsx index ece3844b1b..3e729ef4e8 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/Visualiser.tsx @@ -1,8 +1,7 @@ -import { Box, Button, ButtonGroup, Flex } from "@chakra-ui/react"; +import { Box, Button, ButtonGroup, Flex, Icon } from "@chakra-ui/react"; import Dagre from "@dagrejs/dagre"; import { RemoveFunctions } from "kurtosis-ui-components"; import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef } from "react"; -import { FiPlusCircle } from "react-icons/fi"; import { Background, BackgroundVariant, @@ -18,11 +17,13 @@ import { import { v4 as uuidv4 } from "uuid"; import { EnclaveFullInfo } from "../../../types"; import { KurtosisArtifactNode } from "./KurtosisArtifactNode"; -import { KurtosisExecNode } from "./KurtosisExecNode"; +import { nodeIcons } from "./KurtosisNode"; +import { KurtosisPythonNode } from "./KurtosisPythonNode"; import { KurtosisServiceNode } from "./KurtosisServiceNode"; import { KurtosisShellNode } from "./KurtosisShellNode"; import { generateStarlarkFromGraph, getNodeDependencies } from "./utils"; import { useVariableContext } from "./VariableContextProvider"; +import "./Visualiser.css"; const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); const getLayoutedElements = (nodes: Node[], edges: Edge[]) => { @@ -52,7 +53,7 @@ const nodeTypes = { serviceNode: KurtosisServiceNode, artifactNode: KurtosisArtifactNode, shellNode: KurtosisShellNode, - execNode: KurtosisExecNode, + pythonNode: KurtosisPythonNode, }; export type VisualiserImperativeAttributes = { @@ -97,6 +98,9 @@ export const Visualiser = forwardRef { + const handleAddPythonNode = () => { const id = uuidv4(); updateData(id, { - type: "exec", - execName: "", - serviceName: "", + type: "python", + pythonName: "", command: "", - acceptableCodes: [], + packages: [], + image: "", + args: [], + files: [], + store: "", + wait_enabled: "true", + wait: "", isValid: false, }); addNodes({ id, position: getNewNodePosition(), - width: 400, - style: { width: "400px" }, - type: "execNode", + width: 650, + style: { width: "650px" }, + type: "pythonNode", data: {}, }); }; @@ -181,6 +190,7 @@ export const Visualiser = forwardRef - - - - diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx index 83db6c565c..01ea781f93 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MentionStringArgumentInput.tsx @@ -37,7 +37,6 @@ export const MentionStringArgumentInput = ({ return ( { @@ -54,11 +53,12 @@ export const MentionStringArgumentInput = ({ tabIndex={tabIndex} singleLine value={field.value} + disabled={disabled} onChange={(e, newValue, newPlainTextValue, mentions) => field.onChange(newValue)} > diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MountArtifactFileInput.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MountArtifactFileInput.tsx index 8f0631044b..c1bfaa8b28 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MountArtifactFileInput.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/input/MountArtifactFileInput.tsx @@ -10,7 +10,7 @@ export const MountArtifactFileInput = (props: KurtosisFormInputProps { return variables - .filter((variable) => variable.id.match(/^(?:artifact|shell)\.[^.]+$/)) + .filter((variable) => variable.id.match(/^(?:artifact|shell|python)\.[^.]+$/)) .map((variable) => ({ display: variable.displayName, value: `{{${variable.id}}}` })); }, [variables]); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts index b11df47b8e..611fecbc44 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/types.ts @@ -22,15 +22,6 @@ export type KurtosisAcceptableCode = { value: number; }; -export type KurtosisExecNodeData = { - type: "exec"; - execName: string; - serviceName: string; - command: string; - acceptableCodes: KurtosisAcceptableCode[]; - isValid: boolean; -}; - export type KurtosisServiceNodeData = { type: "service"; serviceName: string; @@ -38,6 +29,9 @@ export type KurtosisServiceNodeData = { env: KurtosisEnvironmentVar[]; ports: KurtosisPort[]; files: KurtosisFileMount[]; + execStepEnabled: "true" | "false"; + execStepCommand: string; + execStepAcceptableCodes: KurtosisAcceptableCode[]; isValid: boolean; }; export type KurtosisArtifactNodeData = { @@ -60,8 +54,25 @@ export type KurtosisShellNodeData = { isValid: boolean; }; +export type KurtosisPythonPackage = { packageName: string }; +export type KurtosisPythonArg = { arg: string }; + +export type KurtosisPythonNodeData = { + type: "python"; + pythonName: string; + command: string; + image: string; + packages: KurtosisPythonPackage[]; + args: KurtosisPythonArg[]; + files: KurtosisFileMount[]; + store: string; + wait_enabled: "true" | "false"; + wait: string; + isValid: boolean; +}; + export type KurtosisNodeData = | KurtosisArtifactNodeData | KurtosisServiceNodeData | KurtosisShellNodeData - | KurtosisExecNodeData; + | KurtosisPythonNodeData; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts index 87e53645a2..3812db21dd 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/enclaveBuilder/utils.ts @@ -45,8 +45,8 @@ export function getNodeName(kurtosisNodeData: KurtosisNodeData): string { if (kurtosisNodeData.type === "shell") { return kurtosisNodeData.shellName; } - if (kurtosisNodeData.type === "exec") { - return kurtosisNodeData.execName; + if (kurtosisNodeData.type === "python") { + return kurtosisNodeData.pythonName; } throw new Error(`Unknown node type.`); } @@ -59,7 +59,11 @@ function escapeString(value: string): string { return value.replaceAll(/(["\\])/g, "\\$1"); } -const variablePattern = /\{\{((?:service|artifact|shell).([^.]+)\.?.*)}}/; +function escapeTemplateString(value: string): string { + return escapeString(value).replaceAll(/{{(.*?)}}/g, "{{`{{$1}}`}}"); +} + +const variablePattern = /\{\{((?:service|artifact|shell|python).([^.]+)\.?.*?)}}/; export function getVariablesFromNodes(nodes: Record): Variable[] { return Object.entries(nodes).flatMap(([id, data]) => { if (data.type === "service") { @@ -129,6 +133,21 @@ export function getVariablesFromNodes(nodes: Record): ]; } + if (data.type === "python") { + return [ + { + id: `python.${id}`, + displayName: `python.${data.pythonName}`, + value: `${normaliseNameToStarlarkVariable(data.pythonName)}.files_artifacts[0]`, + }, + ...data.args.map((arg, i) => ({ + id: `python.${id}.args.${i}`, + displayName: `python.${data.pythonName}.args[${i}]`, + value: `"${arg.arg}"`, + })), + ]; + } + return []; }); } @@ -165,6 +184,12 @@ export function getNodeDependencies(nodes: Record): Re getDependenciesFor(id).add(fileMatches[2]); } }); + if (data.execStepEnabled === "true") { + const commandMatches = data.execStepCommand.match(variablePattern); + if (commandMatches) { + getDependenciesFor(id).add(commandMatches[2]); + } + } } if (data.type === "shell") { const nameMatches = data.shellName.match(variablePattern); @@ -184,19 +209,23 @@ export function getNodeDependencies(nodes: Record): Re } }); } - if (data.type === "exec") { - const nameMatches = data.execName.match(variablePattern); + if (data.type === "python") { + const nameMatches = data.pythonName.match(variablePattern); if (nameMatches) { getDependenciesFor(id).add(nameMatches[2]); } - const serviceMatches = data.serviceName.match(variablePattern); - if (serviceMatches) { - getDependenciesFor(id).add(serviceMatches[2]); - } - const commandMatches = data.command.match(variablePattern); - if (commandMatches) { - getDependenciesFor(id).add(commandMatches[2]); - } + data.args.forEach((arg) => { + const argMatches = arg.arg.match(variablePattern); + if (argMatches) { + getDependenciesFor(id).add(argMatches[2]); + } + }); + data.files.forEach((file) => { + const fileMatches = file.mountPoint.match(variablePattern) || file.artifactName.match(variablePattern); + if (fileMatches) { + getDependenciesFor(id).add(fileMatches[2]); + } + }); } }); return dependencies; @@ -210,17 +239,18 @@ export function generateStarlarkFromGraph( ): string { // Topological sort const sortedNodes: Node[] = []; - let remainingEdges = [...edges]; + let remainingEdges = [...edges].filter((e) => e.target !== e.source); while (remainingEdges.length > 0 || sortedNodes.length !== nodes.length) { - const nodesRemoved = nodes + const nodesToRemove = nodes .filter((node) => remainingEdges.every((edge) => edge.target !== node.id)) // eslint-disable-line no-loop-func .filter((node) => !sortedNodes.includes(node)); - if (nodesRemoved.length === 0) { + + if (nodesToRemove.length === 0) { throw new Error( "Topological sort couldn't remove nodes. This indicates a cycle has been detected. Starlark cannot be rendered.", ); } - sortedNodes.push(...nodesRemoved); + sortedNodes.push(...nodesToRemove); remainingEdges = remainingEdges.filter((edge) => sortedNodes.every((node) => edge.source !== node.id)); } const variablesById = getVariablesFromNodes(data).reduce( @@ -277,6 +307,21 @@ export function generateStarlarkFromGraph( starlark += ` },\n`; starlark += ` ),\n`; starlark += ` )\n\n`; + + if (nodeData.execStepEnabled === "true") { + const execName = `${serviceName}_exec`; + starlark += ` ${execName} = plan.exec(\n`; + starlark += ` service_name = ${interpolateValue(nodeData.serviceName)},\n`; + starlark += ` recipe = ExecRecipe(\n`; + starlark += ` command = [${nodeData.execStepCommand.split(" ").map(interpolateValue).join(", ")}],`; + starlark += ` ),\n`; + if (nodeData.execStepAcceptableCodes.length > 0) { + starlark += ` acceptable_codes = [${nodeData.execStepAcceptableCodes + .map(({ value }) => value) + .join(", ")}],\n`; + } + starlark += ` )\n\n`; + } } if (nodeData.type === "artifact") { @@ -286,7 +331,7 @@ export function generateStarlarkFromGraph( starlark += ` config = {\n`; for (const [fileName, fileText] of Object.entries(nodeData.files)) { starlark += ` "${fileName}": struct(\n`; - starlark += ` template="""${escapeString(fileText)}""",\n`; + starlark += ` template = """${escapeTemplateString(fileText)}""",\n`; starlark += ` data={},\n`; starlark += ` ),\n`; } @@ -322,15 +367,37 @@ export function generateStarlarkFromGraph( starlark += ` )\n\n`; } - if (nodeData.type === "exec") { - const execName = normaliseNameToStarlarkVariable(nodeData.execName); - starlark += ` ${execName} = plan.exec(\n`; - starlark += ` service_name = ${interpolateValue(nodeData.serviceName)},\n`; - starlark += ` recipe = ExecRecipe(\n`; - starlark += ` command = [${nodeData.command.split(" ").map(interpolateValue).join(", ")}],`; - starlark += ` ),\n`; - if (nodeData.acceptableCodes.length > 0) { - starlark += ` acceptable_codes = [${nodeData.acceptableCodes.map(({ value }) => value).join(", ")}],\n`; + if (nodeData.type === "python") { + const pythonName = normaliseNameToStarlarkVariable(nodeData.pythonName); + starlark += ` ${pythonName} = plan.run_python(\n`; + starlark += ` run = """${escapeString(nodeData.command)}""",\n`; + const image = interpolateValue(nodeData.image); + if (image !== '""') { + starlark += ` image = ${image},\n`; + } + starlark += ` packages = [\n`; + for (const { packageName } of nodeData.packages) { + starlark += ` ${interpolateValue(packageName)},\n`; + } + starlark += ` ],\n`; + starlark += ` args = [\n`; + for (const { arg } of nodeData.args) { + starlark += ` ${interpolateValue(arg)},\n`; + } + starlark += ` ],\n`; + starlark += ` files = {\n`; + for (const { mountPoint, artifactName } of nodeData.files) { + starlark += ` ${interpolateValue(mountPoint)}: ${interpolateValue(artifactName)},\n`; + } + starlark += ` },\n`; + if (nodeData.store !== "") { + starlark += ` store = [\n`; + starlark += ` StoreSpec(src = ${interpolateValue(nodeData.store)}, name="${pythonName}"),\n`; + starlark += ` ],\n`; + } + const wait = interpolateValue(nodeData.wait); + if (nodeData.wait_enabled === "false" || wait !== '""') { + starlark += ` wait=${nodeData.wait_enabled === "true" ? wait : "None"},\n`; } starlark += ` )\n\n`; } From 4048368f9fe38d28f1e912a108b79cdbac17a1b7 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Fri, 16 Feb 2024 17:59:27 +0530 Subject: [PATCH 070/102] fix: allow for single quotes in run_python (#2172) Closes #2171 --- .../kurtosis_instruction/tasks/run_python.go | 6 +++--- .../run_python_test.go | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go index e23aa1e328..87c3e508cf 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go @@ -362,9 +362,9 @@ func getPythonCommandToRun(builtin *RunPythonCapabilities) (string, error) { maybePythonArgumentsWithRuntimeValueReplaced = append(maybePythonArgumentsWithRuntimeValueReplaced, maybePythonArgumentWithRuntimeValueReplaced) } argumentsAsString := strings.Join(maybePythonArgumentsWithRuntimeValueReplaced, spaceDelimiter) - + runEscaped := strings.ReplaceAll(builtin.run, `"`, `\"`) if len(argumentsAsString) > 0 { - return fmt.Sprintf("python -c '%s' %s", builtin.run, argumentsAsString), nil + return fmt.Sprintf(`python -c "%s" %s`, runEscaped, argumentsAsString), nil } - return fmt.Sprintf("python -c '%s'", builtin.run), nil + return fmt.Sprintf(`python -c "%s"`, runEscaped), nil } diff --git a/internal_testsuites/golang/testsuite/starlark_run_python_test/run_python_test.go b/internal_testsuites/golang/testsuite/starlark_run_python_test/run_python_test.go index 526ce54b9c..895c323fae 100644 --- a/internal_testsuites/golang/testsuite/starlark_run_python_test/run_python_test.go +++ b/internal_testsuites/golang/testsuite/starlark_run_python_test/run_python_test.go @@ -36,6 +36,17 @@ print(sys.argv[1]) packages = ["requests"], args = ["Kurtosis"] ) +` + runPythonWithSingleQuotesTest = "run-python-single-quote" + runPythonWithSingleQuotesScript = ` +def run(plan): + python_script = """ +print('kurtosis') + """ + + plan.run_python( + run = python_script, + ) ` ) @@ -54,3 +65,11 @@ func TestStarlark_RunPythonWithExternalPacakges(t *testing.T) { expectedOutput := "Command returned with exit code '0' and the following output:\n--------------------\n200\nKurtosis\n\n--------------------\n" require.Equal(t, expectedOutput, string(runResult.RunOutput)) } + +func TestStarlark_RunPythonWithSingleQuotes(t *testing.T) { + ctx := context.Background() + runResult, err := test_helpers.SetupSimpleEnclaveAndRunScript(t, ctx, runPythonWithSingleQuotesTest, runPythonWithSingleQuotesScript) + require.Nil(t, err) + expectedOutput := "Command returned with exit code '0' and the following output:\n--------------------\nkurtosis\n\n--------------------\n" + require.Equal(t, expectedOutput, string(runResult.RunOutput)) +} From acdd42d23fa079be3fb5a64a80a6503866ca18e1 Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Fri, 16 Feb 2024 07:55:11 -0500 Subject: [PATCH 071/102] chore(main): release 0.86.25 (#2169) :robot: I have created a release *beep* *boop* --- ## [0.86.25](https://github.com/kurtosis-tech/kurtosis/compare/0.86.24...0.86.25) (2024-02-16) ### Features * emui builder python nodes ([#2170](https://github.com/kurtosis-tech/kurtosis/issues/2170)) ([26ad571](https://github.com/kurtosis-tech/kurtosis/commit/26ad5716548edec84e5276ddfe972203c7d5838d)) * kurtosis run `--non-blocking-tasks` ([#2153](https://github.com/kurtosis-tech/kurtosis/issues/2153)) ([6c70247](https://github.com/kurtosis-tech/kurtosis/commit/6c70247531eb0343434101fe54cf6ef028ded873)) ### Bug Fixes * allow for single quotes in run_python ([#2172](https://github.com/kurtosis-tech/kurtosis/issues/2172)) ([4048368](https://github.com/kurtosis-tech/kurtosis/commit/4048368f9fe38d28f1e912a108b79cdbac17a1b7)), closes [#2171](https://github.com/kurtosis-tech/kurtosis/issues/2171) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 13 +++++++++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- .../src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- .../web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 24 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da20b85f25..263046da4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [0.86.25](https://github.com/kurtosis-tech/kurtosis/compare/0.86.24...0.86.25) (2024-02-16) + + +### Features + +* emui builder python nodes ([#2170](https://github.com/kurtosis-tech/kurtosis/issues/2170)) ([26ad571](https://github.com/kurtosis-tech/kurtosis/commit/26ad5716548edec84e5276ddfe972203c7d5838d)) +* kurtosis run `--non-blocking-tasks` ([#2153](https://github.com/kurtosis-tech/kurtosis/issues/2153)) ([6c70247](https://github.com/kurtosis-tech/kurtosis/commit/6c70247531eb0343434101fe54cf6ef028ded873)) + + +### Bug Fixes + +* allow for single quotes in run_python ([#2172](https://github.com/kurtosis-tech/kurtosis/issues/2172)) ([4048368](https://github.com/kurtosis-tech/kurtosis/commit/4048368f9fe38d28f1e912a108b79cdbac17a1b7)), closes [#2171](https://github.com/kurtosis-tech/kurtosis/issues/2171) + ## [0.86.24](https://github.com/kurtosis-tech/kurtosis/compare/0.86.23...0.86.24) (2024-02-15) diff --git a/LICENSE.md b/LICENSE.md index 415beffcb4..30f893d163 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.24 +Licensed Work: Kurtosis 0.86.25 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-02-15 +Change Date: 2028-02-16 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 9fc7a08365..e34f7afd25 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.24" + KurtosisVersion = "0.86.25" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 2affd81e7f..e6f058ecc6 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.24" +version = "0.86.25" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index cd1e66154e..9beff6b9ce 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.24", + "version": "0.86.25", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index bb707b6607..d563a0ed50 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.24" +export const KURTOSIS_VERSION: string = "0.86.25" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 22c5942cec..fad6d364c2 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.24", + "version": "0.86.25", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index 0d056c142e..fe6a14d200 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.24", + "version": "0.86.25", "private": true, "homepage": ".", "dependencies": { @@ -10,7 +10,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.24", + "kurtosis-ui-components": "0.86.25", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "react-mentions": "^4.4.10", diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index 52d18682a6..d2a402f395 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.24", + "version": "0.86.25", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index df15585f8d..99bbd775e0 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.24 +0.86.25 From d7fdbc5fa56f81a9194a68f37d1ab9549dc45abd Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Fri, 16 Feb 2024 22:03:21 +0530 Subject: [PATCH 072/102] fix: improved some descriptions for starlark instructions (#2168) Kevin had given some feedback on #2147 ; this PR works on those --- .../kurtosis_instruction/tasks/run_python.go | 2 +- .../kurtosis_instruction/tasks/run_sh.go | 9 +++++++-- .../kurtosis_instruction/upload_files/upload_files.go | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go index 87c3e508cf..5ebd17ac9f 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_python.go @@ -328,7 +328,7 @@ func (builtin *RunPythonCapabilities) FillPersistableAttributes(builder *enclave } func (builtin *RunPythonCapabilities) Description() string { - return "Running a one time python script" + return "Running Python script" } func setupRequiredPackages(ctx context.Context, builtin *RunPythonCapabilities) (*exec_result.ExecResult, error) { diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go index 4935dacb60..324fe2024b 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/tasks/run_sh.go @@ -26,7 +26,9 @@ import ( const ( RunShBuiltinName = "run_sh" - defaultRunShImageName = "badouralix/curl-jq" + defaultRunShImageName = "badouralix/curl-jq" + shScriptPrintCharLimit = 80 + runningShScriptPrefix = "Running sh script" ) func NewRunShService(serviceNetwork service_network.ServiceNetwork, runtimeValueStore *runtime_value_store.RuntimeValueStore, nonBlockingMode bool) *kurtosis_plan_instruction.KurtosisPlanInstruction { @@ -273,7 +275,10 @@ func (builtin *RunShCapabilities) FillPersistableAttributes(builder *enclave_pla } func (builtin *RunShCapabilities) Description() string { - return "Running a one time bash script" + if len(builtin.run) < shScriptPrintCharLimit { + return fmt.Sprintf("%v: `%v`", runningShScriptPrefix, builtin.run) + } + return runningShScriptPrefix } func getCommandToRun(builtin *RunShCapabilities) (string, error) { diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go index 8c5a9e932a..0178ec8e21 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go @@ -215,5 +215,5 @@ func (builtin *UploadFilesCapabilities) FillPersistableAttributes(builder *encla } func (builtin *UploadFilesCapabilities) Description() string { - return fmt.Sprintf("Uploading file '%v' to files articact '%v'", builtin.src, builtin.artifactName) + return fmt.Sprintf("Uploading file '%v' to files artifact '%v'", builtin.src, builtin.artifactName) } From d3b3de9b6cb7ff0a83488ccbf8756dfb77814c9c Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Sat, 17 Feb 2024 00:36:23 +0530 Subject: [PATCH 073/102] fix: change default verbosity to description (#2173) --- cli/cli/commands/run/run.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/cli/commands/run/run.go b/cli/cli/commands/run/run.go index c751d50c91..e271c849ae 100644 --- a/cli/cli/commands/run/run.go +++ b/cli/cli/commands/run/run.go @@ -68,7 +68,7 @@ const ( autogenerateEnclaveIdentifierKeyword = "" verbosityFlagKey = "verbosity" - defaultVerbosity = "brief" + defaultVerbosity = "description" parallelismFlagKey = "parallelism" defaultParallelism = "4" @@ -143,7 +143,7 @@ var StarlarkRunCmd = &engine_consuming_kurtosis_command.EngineConsumingKurtosisC }, { Key: verbosityFlagKey, - Usage: fmt.Sprintf("The verbosity of the command output: %s. If unset, it defaults to `brief` for a concise and explicit output. Use `detailed` to display the exhaustive list of arguments for each command. `executable` will generate executable Starlark instructions. `description` will just print a description of what is about to happen without any details", strings.Join(command_args_run.VerbosityStrings(), ", ")), + Usage: fmt.Sprintf("The verbosity of the command output: %s. If unset, it defaults to `description` for a crisp output that explains whats about to happen. Use `brief` for a concise yet explicit ouptut, to see the entire instruction thats about to execute. Use `detailed` to display the exhaustive list of arguments for each instruction. `executable` will generate executable Starlark instructions.", strings.Join(command_args_run.VerbosityStrings(), ", ")), Type: flags.FlagType_String, Shorthand: "v", Default: defaultVerbosity, From 83765db973b99be48f5e88933547a33012d1f202 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Mon, 19 Feb 2024 14:21:14 +0000 Subject: [PATCH 074/102] docs: added wait to service config (#2179) Closes #2125 --- .../api-reference/starlark-reference/service-config.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/docs/api-reference/starlark-reference/service-config.md b/docs/docs/api-reference/starlark-reference/service-config.md index 11afaa6592..7c771ad3ae 100644 --- a/docs/docs/api-reference/starlark-reference/service-config.md +++ b/docs/docs/api-reference/starlark-reference/service-config.md @@ -91,6 +91,16 @@ config = ServiceConfig( # Application protocol for the port # Optional application_protocol = "http", + + # Kurtosis will automatically perform a check to ensure all declared UDP and TCP ports are open and ready for traffic and connections upon startup. + # You may specify a custom wait timeout duration or disable the feature entirely. + # You may specify a custom wait timeout duration with a string: + # wait = "2m" + # Or, you can disable this feature by setting the value to None: + # wait = None + # The feature is enabled by default with a default timeout of 15s + # OPTIONAL (DEFAULT:"15s") + wait = "4s" ), }, From aafdc0d0edec47d297f9112c4418a0854fba5b41 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Mon, 19 Feb 2024 15:28:19 +0000 Subject: [PATCH 075/102] ci: proto break change doesnt run all the time (#2130) --- .github/workflows/check-proto-break.yml | 18 ++++++++++++++++++ .github/workflows/gofmt.yml | 18 ++++++++++++++++++ .github/workflows/golangci-lint.yml | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/.github/workflows/check-proto-break.yml b/.github/workflows/check-proto-break.yml index d293d29524..d1aeac0c97 100644 --- a/.github/workflows/check-proto-break.yml +++ b/.github/workflows/check-proto-break.yml @@ -8,7 +8,25 @@ on: - edited jobs: + check-if-code-change: + runs-on: ubuntu-latest + outputs: + change: ${{ steps.check.outputs.change }} + steps: + - uses: actions/checkout@v4 + with: + go-version: '1.20' + fetch-depth: 0 + - run: | + if git --no-pager diff --exit-code origin/main...HEAD -- . ':!docs' ':!*.md'; then + echo "::set-output name=change::false" + else + echo "::set-output name=change::true" + fi + id: check check-proto-break: + needs: check-if-code-change + if: ${{ needs.check-if-code-change.outputs.change == 'true' }} runs-on: ubuntu-latest steps: - uses: actions/setup-go@v3 diff --git a/.github/workflows/gofmt.yml b/.github/workflows/gofmt.yml index f9da39a006..9a558047c4 100644 --- a/.github/workflows/gofmt.yml +++ b/.github/workflows/gofmt.yml @@ -11,7 +11,25 @@ on: merge_group: jobs: + check-if-code-change: + runs-on: ubuntu-latest + outputs: + change: ${{ steps.check.outputs.change }} + steps: + - uses: actions/checkout@v4 + with: + go-version: '1.20' + fetch-depth: 0 + - run: | + if git --no-pager diff --exit-code origin/main...HEAD -- . ':!docs' ':!*.md'; then + echo "::set-output name=change::false" + else + echo "::set-output name=change::true" + fi + id: check gofmt: + needs: check-if-code-change + if: ${{ needs.check-if-code-change.outputs.change == 'true' }} name: gofmt runs-on: ubuntu-latest steps: diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 7d837605f4..7c8da0864d 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -21,7 +21,25 @@ env: GOLINT_VERSION: v1.53.3 jobs: + check-if-code-change: + runs-on: ubuntu-latest + outputs: + change: ${{ steps.check.outputs.change }} + steps: + - uses: actions/checkout@v4 + with: + go-version: '1.20' + fetch-depth: 0 + - run: | + if git --no-pager diff --exit-code origin/main...HEAD -- . ':!docs' ':!*.md'; then + echo "::set-output name=change::false" + else + echo "::set-output name=change::true" + fi + id: check golangci: + needs: check-if-code-change + if: ${{ needs.check-if-code-change.outputs.change == 'true' }} name: golang-lint runs-on: ubuntu-latest steps: From 8fe42560fda59d1b5b44882357a483f815e6a722 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Mon, 19 Feb 2024 18:31:05 +0000 Subject: [PATCH 076/102] docs: renamed idempotent runs label (#2180) Closes #1960 --- docs/docs/advanced-concepts/how-do-idempotent-runs-work.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/advanced-concepts/how-do-idempotent-runs-work.md b/docs/docs/advanced-concepts/how-do-idempotent-runs-work.md index c8ee0a167a..4408c00c5a 100644 --- a/docs/docs/advanced-concepts/how-do-idempotent-runs-work.md +++ b/docs/docs/advanced-concepts/how-do-idempotent-runs-work.md @@ -1,6 +1,6 @@ --- title: How do idempotent runs work? -sidebar_label: Idempotent Runs +sidebar_label: How do idempotent runs work? --- Background From 660ff158de7b1002d3d5fab10b9670a8957ed90f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 10:49:10 +0000 Subject: [PATCH 077/102] build(deps): Bump github.com/go-git/go-git/v5 from 5.4.2 to 5.11.0 in /core/server (#2003) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.4.2 to 5.11.0.
Release notes

Sourced from github.com/go-git/go-git/v5's releases.

v5.11.0

What's Changed

New Contributors

Full Changelog: https://github.com/go-git/go-git/compare/v5.10.1...v5.11.0

v5.10.1

What's Changed

New Contributors

Full Changelog: https://github.com/go-git/go-git/compare/v5.10.0...v5.10.1

v5.10.0

What's Changed

... (truncated)

Commits
  • 5d08d3b Merge pull request #958 from pjbgf/workval
  • 5bd1d8f build: Ensure checkout is the first operation
  • b2c1982 git: worktree, Align validation with upstream rules
  • cec7da6 Merge pull request #953 from pjbgf/alternates
  • 8b47ceb storage: filesystem, Add option to set a specific FS for alternates
  • 4f61489 Merge pull request #941 from djmoch/filestats-rename
  • ae552ce Merge pull request #939 from dhoizner/fix-pull-after-shallow
  • cc1895b Merge pull request #950 from aymanbagabas/validate-ref
  • de1d5a5 git: validate reference names
  • d87110b Merge pull request #948 from go-git/dependabot/go_modules/cli/go-git/github.c...
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/go-git/go-git/v5&package-manager=go_modules&previous-version=5.4.2&new-version=5.11.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/kurtosis-tech/kurtosis/network/alerts).
> **Note** > Automatic rebases have been disabled on this pull request as it has been open for over 30 days. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gyanendra Mishra --- core/server/go.mod | 25 ++++++---- core/server/go.sum | 122 ++++++++++++++++++++++++--------------------- 2 files changed, 80 insertions(+), 67 deletions(-) diff --git a/core/server/go.mod b/core/server/go.mod index 7bae767697..2501f67602 100644 --- a/core/server/go.mod +++ b/core/server/go.mod @@ -32,7 +32,7 @@ require ( require ( github.com/compose-spec/compose-go v1.17.0 - github.com/go-git/go-git/v5 v5.4.2 + github.com/go-git/go-git/v5 v5.11.0 github.com/go-yaml/yaml v2.1.0+incompatible github.com/itchyny/gojq v0.12.9 github.com/joho/godotenv v1.5.1 @@ -50,15 +50,16 @@ require ( require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect + dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect - github.com/acomagu/bufpipe v1.0.3 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/bytedance/sonic v1.10.0-rc3 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/containerd/containerd v1.7.2 // indirect github.com/containerd/typeurl/v2 v2.1.1 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa // indirect @@ -68,12 +69,12 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/emicklei/go-restful/v3 v3.10.1 // indirect - github.com/emirpasic/gods v1.12.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect github.com/gammazero/deque v0.1.0 // indirect github.com/gammazero/workerpool v1.1.2 // indirect github.com/gin-gonic/gin v1.9.1 // indirect - github.com/go-git/gcfg v1.5.0 // indirect - github.com/go-git/go-billy/v5 v5.3.1 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect @@ -82,10 +83,11 @@ require ( github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect @@ -95,14 +97,13 @@ require ( github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kurtosis-tech/kurtosis/contexts-config-store v0.0.0-20231024185242-de10c7bab36c // indirect github.com/kurtosis-tech/kurtosis/engine/launcher v0.0.0-20231024185242-de10c7bab36c // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/buildkit v0.12.4 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -116,15 +117,17 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc3 // indirect github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rs/cors v1.9.0 // indirect github.com/segmentio/backo-go v1.0.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect + github.com/skeema/knownhosts v1.2.1 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect - github.com/xanzy/ssh-agent v0.3.0 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect @@ -136,7 +139,7 @@ require ( golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 // indirect + golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect diff --git a/core/server/go.sum b/core/server/go.sum index f111837b70..ded622d98a 100644 --- a/core/server/go.sum +++ b/core/server/go.sum @@ -4,6 +4,8 @@ cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -11,33 +13,27 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -48,7 +44,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= @@ -71,7 +67,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -92,6 +88,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -117,10 +115,11 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -128,7 +127,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -144,17 +142,14 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= -github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= -github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= -github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= -github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= +github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -206,6 +201,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -238,8 +235,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -285,7 +282,6 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= @@ -298,7 +294,6 @@ github.com/itchyny/timefmt-go v0.1.4 h1:hFEfWVdwsEi+CY8xY2FtgWHGQaBaC3JeHd+cve0y github.com/itchyny/timefmt-go v0.1.4/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= @@ -316,8 +311,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -358,8 +353,6 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -374,8 +367,6 @@ github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -411,7 +402,6 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= @@ -422,7 +412,7 @@ github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -446,6 +436,8 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -481,8 +473,8 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= @@ -496,12 +488,13 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -544,8 +537,8 @@ github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -559,6 +552,7 @@ github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEAB github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= @@ -589,15 +583,15 @@ golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -618,6 +612,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -642,10 +638,14 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= @@ -657,6 +657,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -672,42 +674,49 @@ golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -732,6 +741,8 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -789,7 +800,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= From 150cafc2bc52608be5f02cd0b3fd5c19cc6687e4 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Tue, 20 Feb 2024 14:50:40 +0000 Subject: [PATCH 078/102] build: make version check script check for version 20.11 (#2192) We are currently checking for 16.x and then asserting that we need 20 --- scripts/versions_check.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/versions_check.sh b/scripts/versions_check.sh index 30b3ff0184..67c17f83ed 100755 --- a/scripts/versions_check.sh +++ b/scripts/versions_check.sh @@ -6,7 +6,7 @@ set -euo pipefail # Bash "strict mode" GO_VERSION=1.20 # FOR NODE, WE PIN THE EXACT VERSION NUMBER -NODE_VERSION=16.14 +NODE_VERSION=20.11 RED_BG=$(tput setab 1) From bc20d51fd8fb7d02a8270586dcd4be85564cb415 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Tue, 20 Feb 2024 15:15:03 +0000 Subject: [PATCH 079/102] feat!: change registry spec to imagespec (#2191) this is cleaner; registry is an implementation detail that the end person doesn't care about Closes #2189 --- .../objects/service/service_config_test.go | 2 +- .../startosis_engine/kurtosis_builtins.go | 2 +- .../service_config_image_spec_minimal_test.go | 80 ++++++++++++++ ...t.go => service_config_image_spec_test.go} | 24 ++--- .../{image_registry_spec.go => image_spec.go} | 102 +++++++++--------- .../service_config/service_config.go | 8 +- .../starlark-reference/service-config.md | 44 ++++---- 7 files changed, 171 insertions(+), 91 deletions(-) create mode 100644 core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_spec_minimal_test.go rename core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/{service_config_image_registry_spec_test.go => service_config_image_spec_test.go} (81%) rename core/server/api_container/server/startosis_engine/kurtosis_types/service_config/{image_registry_spec.go => image_spec.go} (57%) diff --git a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go index 3f3df193f8..c5fa38704d 100644 --- a/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go +++ b/container-engine-lib/lib/backend_interface/objects/service/service_config_test.go @@ -2,11 +2,11 @@ package service import ( "encoding/json" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "testing" "time" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_directory" diff --git a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go index 1afae0c723..fc8df636b6 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_builtins.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_builtins.go @@ -114,7 +114,7 @@ func KurtosisTypeConstructors() []*starlark.Builtin { starlark.NewBuiltin(service_config.ReadyConditionTypeName, service_config.NewReadyConditionType().CreateBuiltin()), starlark.NewBuiltin(service_config.ImageBuildSpecTypeName, service_config.NewImageBuildSpecType().CreateBuiltin()), starlark.NewBuiltin(service_config.NixBuildSpecTypeName, service_config.NewNixBuildSpecType().CreateBuiltin()), - starlark.NewBuiltin(service_config.ImageRegistrySpecTypeName, service_config.NewImageRegistrySpec().CreateBuiltin()), + starlark.NewBuiltin(service_config.ImageSpecTypeName, service_config.NewImageSpec().CreateBuiltin()), starlark.NewBuiltin(service_config.UserTypeName, service_config.NewUserType().CreateBuiltin()), starlark.NewBuiltin(service_config.TolerationTypeName, service_config.NewTolerationType().CreateBuiltin()), } diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_spec_minimal_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_spec_minimal_test.go new file mode 100644 index 0000000000..13698b4ff8 --- /dev/null +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_spec_minimal_test.go @@ -0,0 +1,80 @@ +package test_engine + +import ( + "fmt" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" + "testing" + + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/builtin_argument" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/kurtosis_types/service_config" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages" + "github.com/stretchr/testify/require" +) + +type serviceConfigImageSpecMinimalTest struct { + *testing.T + serviceNetwork *service_network.MockServiceNetwork + packageContentProvider *startosis_packages.MockPackageContentProvider +} + +func (suite *KurtosisTypeConstructorTestSuite) TestServiceConfigWithImageSpecImageOnly() { + suite.run(&serviceConfigImageSpecMinimalTest{ + T: suite.T(), + serviceNetwork: suite.serviceNetwork, + packageContentProvider: suite.packageContentProvider, + }) +} + +func (t *serviceConfigImageSpecMinimalTest) GetStarlarkCode() string { + imageSpec := fmt.Sprintf("%s(%s=%q)", + service_config.ImageSpecTypeName, + service_config.ImageAttr, + testContainerImageName, + ) + return fmt.Sprintf("%s(%s=%s)", + service_config.ServiceConfigTypeName, + service_config.ImageAttr, imageSpec) +} + +func (t *serviceConfigImageSpecMinimalTest) Assert(typeValue builtin_argument.KurtosisValueType) { + serviceConfigStarlark, ok := typeValue.(*service_config.ServiceConfig) + require.True(t, ok) + + serviceConfig, interpretationErr := serviceConfigStarlark.ToKurtosisType( + t.serviceNetwork, + testModuleMainFileLocator, + testModulePackageId, + t.packageContentProvider, + testNoPackageReplaceOptions) + require.Nil(t, interpretationErr) + + expectedImageRegistrySpec := image_registry_spec.NewImageRegistrySpec(testContainerImageName, "", "", "") + expectedServiceConfig, err := service.CreateServiceConfig( + testContainerImageName, + nil, + expectedImageRegistrySpec, + nil, + map[string]*port_spec.PortSpec{}, + map[string]*port_spec.PortSpec{}, + nil, + nil, + map[string]string{}, + nil, + nil, + 0, + 0, + service_config.DefaultPrivateIPAddrPlaceholder, + 0, + 0, + map[string]string{}, + nil, + nil, + map[string]string{}, + ) + require.NoError(t, err) + require.Equal(t, expectedServiceConfig, serviceConfig) + require.Equal(t, expectedImageRegistrySpec, serviceConfig.GetImageRegistrySpec()) +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_spec_test.go similarity index 81% rename from core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go rename to core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_spec_test.go index af3fa83ba2..d07e6a1ff6 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_registry_spec_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_spec_test.go @@ -2,9 +2,9 @@ package test_engine import ( "fmt" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "testing" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/service_network" @@ -14,38 +14,38 @@ import ( "github.com/stretchr/testify/require" ) -type serviceConfigImageRegistrySpecTest struct { +type serviceConfigImageSpecTest struct { *testing.T serviceNetwork *service_network.MockServiceNetwork packageContentProvider *startosis_packages.MockPackageContentProvider } -func (suite *KurtosisTypeConstructorTestSuite) TestServiceConfigWithImageRegistrySpec() { - suite.run(&serviceConfigImageRegistrySpecTest{ +func (suite *KurtosisTypeConstructorTestSuite) TestServiceConfigWithImageSpecWithRegistry() { + suite.run(&serviceConfigImageSpecTest{ T: suite.T(), serviceNetwork: suite.serviceNetwork, packageContentProvider: suite.packageContentProvider, }) } -func (t *serviceConfigImageRegistrySpecTest) GetStarlarkCode() string { - imageRegistrySpec := fmt.Sprintf("%s(%s=%q, %s=%q, %s=%q, %s=%q)", - service_config.ImageRegistrySpecTypeName, +func (t *serviceConfigImageSpecTest) GetStarlarkCode() string { + imageSpec := fmt.Sprintf("%s(%s=%q, %s=%q, %s=%q, %s=%q)", + service_config.ImageSpecTypeName, service_config.ImageAttr, testContainerImageName, - service_config.RegistryAddrAttr, + service_config.ImageRegistryAttr, testRegistryAddr, - service_config.RegistryUsernameAttr, + service_config.ImageRegistryUsernameAttr, testRegistryUsername, - service_config.RegistryPasswordAttr, + service_config.ImageRegistryPasswordAttr, testRegistryPassword, ) return fmt.Sprintf("%s(%s=%s)", service_config.ServiceConfigTypeName, - service_config.ImageAttr, imageRegistrySpec) + service_config.ImageAttr, imageSpec) } -func (t *serviceConfigImageRegistrySpecTest) Assert(typeValue builtin_argument.KurtosisValueType) { +func (t *serviceConfigImageSpecTest) Assert(typeValue builtin_argument.KurtosisValueType) { serviceConfigStarlark, ok := typeValue.(*service_config.ServiceConfig) require.True(t, ok) diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_registry_spec.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_spec.go similarity index 57% rename from core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_registry_spec.go rename to core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_spec.go index 3957006226..e8094a2a4a 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_registry_spec.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_spec.go @@ -10,49 +10,50 @@ import ( ) const ( - ImageRegistrySpecTypeName = "ImageRegistrySpec" + ImageSpecTypeName = "ImageSpec" - RegistryImageAttr = "image" - RegistryAddrAttr = "registry" - RegistryUsernameAttr = "username" - RegistryPasswordAttr = "password" + ImageSpecImageAttr = "image" + ImageRegistryAttr = "registry" + ImageRegistryUsernameAttr = "username" + ImageRegistryPasswordAttr = "password" ) -func NewImageRegistrySpec() *kurtosis_type_constructor.KurtosisTypeConstructor { +func NewImageSpec() *kurtosis_type_constructor.KurtosisTypeConstructor { + return &kurtosis_type_constructor.KurtosisTypeConstructor{ KurtosisBaseBuiltin: &kurtosis_starlark_framework.KurtosisBaseBuiltin{ - Name: ImageRegistrySpecTypeName, + Name: ImageSpecTypeName, Arguments: []*builtin_argument.BuiltinArgument{ { - Name: RegistryImageAttr, + Name: ImageSpecImageAttr, IsOptional: false, ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], Validator: func(value starlark.Value) *startosis_errors.InterpretationError { - return builtin_argument.NonEmptyString(value, RegistryImageAttr) + return builtin_argument.NonEmptyString(value, ImageSpecImageAttr) }, }, { - Name: RegistryAddrAttr, - IsOptional: false, + Name: ImageRegistryAttr, + IsOptional: true, ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], Validator: func(value starlark.Value) *startosis_errors.InterpretationError { - return builtin_argument.NonEmptyString(value, RegistryAddrAttr) + return builtin_argument.NonEmptyString(value, ImageRegistryAttr) }, }, { - Name: RegistryUsernameAttr, - IsOptional: false, + Name: ImageRegistryUsernameAttr, + IsOptional: true, ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], Validator: func(value starlark.Value) *startosis_errors.InterpretationError { - return builtin_argument.NonEmptyString(value, RegistryUsernameAttr) + return builtin_argument.NonEmptyString(value, ImageRegistryUsernameAttr) }, }, { - Name: RegistryPasswordAttr, - IsOptional: false, + Name: ImageRegistryPasswordAttr, + IsOptional: true, ZeroValueProvider: builtin_argument.ZeroValueProvider[starlark.String], Validator: func(value starlark.Value) *startosis_errors.InterpretationError { - return builtin_argument.NonEmptyString(value, RegistryUsernameAttr) + return builtin_argument.NonEmptyString(value, ImageRegistryUsernameAttr) }, }, }, @@ -62,103 +63,100 @@ func NewImageRegistrySpec() *kurtosis_type_constructor.KurtosisTypeConstructor { } func instantiateImageRegistrySpec(arguments *builtin_argument.ArgumentValuesSet) (builtin_argument.KurtosisValueType, *startosis_errors.InterpretationError) { - kurtosisValueType, err := kurtosis_type_constructor.CreateKurtosisStarlarkTypeDefault(ImageRegistrySpecTypeName, arguments) + kurtosisValueType, err := kurtosis_type_constructor.CreateKurtosisStarlarkTypeDefault(ImageSpecTypeName, arguments) if err != nil { return nil, err } - return &ImageRegistrySpec{ + return &ImageSpec{ KurtosisValueTypeDefault: kurtosisValueType, }, nil } -// ImageRegistrySpec is a starlark.Value that holds all the information to log in to a Docker registry -type ImageRegistrySpec struct { +// ImageSpec is a starlark.Value that holds all the information to log in to a Docker registry +type ImageSpec struct { *kurtosis_type_constructor.KurtosisValueTypeDefault } -func (irs *ImageRegistrySpec) Copy() (builtin_argument.KurtosisValueType, error) { +func (irs *ImageSpec) Copy() (builtin_argument.KurtosisValueType, error) { copiedValueType, err := irs.KurtosisValueTypeDefault.Copy() if err != nil { return nil, err } - return &ImageRegistrySpec{ + return &ImageSpec{ KurtosisValueTypeDefault: copiedValueType, }, nil } // GetImage returns the image that needs to be pulled -func (irs *ImageRegistrySpec) GetImage() (string, *startosis_errors.InterpretationError) { - image, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, RegistryImageAttr) +func (irs *ImageSpec) GetImage() (string, *startosis_errors.InterpretationError) { + image, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, ImageAttr) if interpretationErr != nil { return "", interpretationErr } if !found { return "", startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", - RegistryImageAttr, ImageRegistrySpecTypeName) + ImageAttr, ImageSpecTypeName) } imageStr := image.GoString() return imageStr, nil } -// GetPassword returns the password of the account for the registry -func (irs *ImageRegistrySpec) GetPassword() (string, *startosis_errors.InterpretationError) { - password, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, RegistryPasswordAttr) +// GetPasswordIfSet returns the password of the account for the registry +func (irs *ImageSpec) GetPasswordIfSet() (string, *startosis_errors.InterpretationError) { + password, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, ImageRegistryPasswordAttr) + if !found { + return "", nil + } if interpretationErr != nil { return "", interpretationErr } - if !found { - return "", startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", - RegistryPasswordAttr, ImageRegistrySpecTypeName) - } passwordStr := password.GoString() return passwordStr, nil } -// GetRegistryAddr returns the address of the registry from which the image has to be pulled -func (irs *ImageRegistrySpec) GetRegistryAddr() (string, *startosis_errors.InterpretationError) { - registryAddr, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, RegistryAddrAttr) +// GetRegistryAddrIfSet returns the address of the registry from which the image has to be pulled +func (irs *ImageSpec) GetRegistryAddrIfSet() (string, *startosis_errors.InterpretationError) { + registryAddr, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, ImageRegistryAttr) + if !found { + return "", nil + } if interpretationErr != nil { return "", interpretationErr } - if !found { - return "", startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", - RegistryAddrAttr, ImageRegistrySpecTypeName) - } registryAddrStr := registryAddr.GoString() return registryAddrStr, nil } -// GetUsername returns the address of the registry from which the image has to be pulled -func (irs *ImageRegistrySpec) GetUsername() (string, *startosis_errors.InterpretationError) { - registryAddr, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, RegistryUsernameAttr) +// GetUsernameIfSet returns the address of the registry from which the image has to be pulled +func (irs *ImageSpec) GetUsernameIfSet() (string, *startosis_errors.InterpretationError) { + registryAddr, found, interpretationErr := kurtosis_type_constructor.ExtractAttrValue[starlark.String](irs.KurtosisValueTypeDefault, ImageRegistryUsernameAttr) + if !found { + return "", nil + } if interpretationErr != nil { return "", interpretationErr } - if !found { - return "", startosis_errors.NewInterpretationError("Required attribute '%s' could not be found on type '%s'", - RegistryUsernameAttr, ImageRegistrySpecTypeName) - } registryAddrStr := registryAddr.GoString() return registryAddrStr, nil } -func (irs *ImageRegistrySpec) ToKurtosisType() (*image_registry_spec.ImageRegistrySpec, *startosis_errors.InterpretationError) { +func (irs *ImageSpec) ToKurtosisType() (*image_registry_spec.ImageRegistrySpec, *startosis_errors.InterpretationError) { image, err := irs.GetImage() if err != nil { return nil, err } - username, err := irs.GetUsername() + username, err := irs.GetUsernameIfSet() if err != nil { return nil, err } - password, err := irs.GetPassword() + password, err := irs.GetPasswordIfSet() if err != nil { return nil, err } - registryAddr, err := irs.GetRegistryAddr() + registryAddr, err := irs.GetRegistryAddrIfSet() if err != nil { return nil, err } diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go index 8e014b3608..00afb3c834 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/service_config.go @@ -2,11 +2,11 @@ package service_config import ( "fmt" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "math" "path" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec" - "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/port_spec" "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service" @@ -671,7 +671,7 @@ func convertFilesArguments(attrNameForLogging string, filesDict *starlark.Dict) // If [image] is an ImageBuildSpec type, returns name for the image to build and ImageBuildSpec converted to KurtosisType // If [image] is a string, returns the image name with no image build spec (image will be fetched from local cache or remote) -// If [image] is an ImageRegistrySpec type, returns the name for the image and the ImageRegistrySpec converted to KurtosisType +// If [image] is an ImageSpec type, returns the name for the image and the ImageSpec converted to KurtosisType func convertImage( image starlark.Value, locatorOfModuleInWhichThisBuiltInIsBeingCalled string, @@ -679,7 +679,7 @@ func convertImage( packageContentProvider startosis_packages.PackageContentProvider, packageReplaceOptions map[string]string) (string, *image_build_spec.ImageBuildSpec, *image_registry_spec.ImageRegistrySpec, *nix_build_spec.NixBuildSpec, *startosis_errors.InterpretationError) { imageBuildSpecStarlarkType, isImageBuildSpecStarlarkType := image.(*ImageBuildSpec) - imageRegistrySpecStarlarkType, isImageRegistrySpecStarlarkType := image.(*ImageRegistrySpec) + imageSpecStarlarkType, isImageRegistrySpecStarlarkType := image.(*ImageSpec) nixBuildSpecStarlarkType, isNixBuildSpecStarlarkType := image.(*NixBuildSpec) if isImageBuildSpecStarlarkType { imageBuildSpec, interpretationErr := imageBuildSpecStarlarkType.ToKurtosisType(locatorOfModuleInWhichThisBuiltInIsBeingCalled, packageId, packageContentProvider, packageReplaceOptions) @@ -692,7 +692,7 @@ func convertImage( } return imageName, imageBuildSpec, nil, nil, nil } else if isImageRegistrySpecStarlarkType { - imageRegistrySpec, interpretationErr := imageRegistrySpecStarlarkType.ToKurtosisType() + imageRegistrySpec, interpretationErr := imageSpecStarlarkType.ToKurtosisType() if interpretationErr != nil { return "", nil, nil, nil, interpretationErr } diff --git a/docs/docs/api-reference/starlark-reference/service-config.md b/docs/docs/api-reference/starlark-reference/service-config.md index 7c771ad3ae..bdc9f9e7fa 100644 --- a/docs/docs/api-reference/starlark-reference/service-config.md +++ b/docs/docs/api-reference/starlark-reference/service-config.md @@ -11,13 +11,33 @@ config = ServiceConfig( # This image can be pulled from a remote container registry, picked up from the local cache, or built by Kurtosis using # the underlying container engine. # If a string is provided, Kurtosis will by default detect if images exists locally, or pull from container registry if not. - # If an ImageBuildSpec is provided, Kurtosis will build the image. - # If an ImageRegistrySpec is provided, Kurtosis will pull the image from the given registry with the given credentials - # Note for now ImageRegistrySpec is Docker only and the authentication gets ignored on Kubernetes + # The string form is syntactic sugar for ImageSpec with only image set + # ImageSpec referring to private registries limited to Docker # Reach out to the team if you want to run Kurtosis with private images on Kubernetes + # If an ImageBuildSpec is provided, Kurtosis will build the image. # MANDATORY image = "kurtosistech/example-datastore-server", + OR + + image = ImageSpec( + # The name of the image that needs to be pulled qualified with the registry + # MANDATORY + name = "my.registry.io/my-user/my-image", + + # The username that will be used to pull the image from the given registry + # OPTIONAL + username = "my-user", + + # The password that will be used to pull the image from the given registry + # OPTIONAL + password = "password", + + # The URL of the registry + # OPTIONAL + registry = "http://my.registry.io/" + ) + OR image = ImageBuildSpec( @@ -34,24 +54,6 @@ config = ServiceConfig( # OPTIONAL target_stage="" ) - - OR - - image = ImageRegistrySpec( - # The name of the image that needs to be pulled qualified with the registry - # MANDATORY - name = "my.registry.io/my-user/my-image", - - # The username that will be used to pull the image from the given registry - # MANDATORY - username = "my-user", - - # The password that will be used to pull the image from the given registry - password = "password", - - # The URL of the registry - registry = "http://my.registry.io/" - ) OR From 93853be50411fd7e902af5e24fc6a283cd0cffcf Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Tue, 20 Feb 2024 10:42:10 -0500 Subject: [PATCH 080/102] chore(main): release 0.87.0 (#2175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* --- ## [0.87.0](https://github.com/kurtosis-tech/kurtosis/compare/0.86.25...0.87.0) (2024-02-20) ### ⚠ BREAKING CHANGES * change registry spec to imagespec ([#2191](https://github.com/kurtosis-tech/kurtosis/issues/2191)) ### Features * change registry spec to imagespec ([#2191](https://github.com/kurtosis-tech/kurtosis/issues/2191)) ([bc20d51](https://github.com/kurtosis-tech/kurtosis/commit/bc20d51fd8fb7d02a8270586dcd4be85564cb415)), closes [#2189](https://github.com/kurtosis-tech/kurtosis/issues/2189) ### Bug Fixes * change default verbosity to description ([#2173](https://github.com/kurtosis-tech/kurtosis/issues/2173)) ([d3b3de9](https://github.com/kurtosis-tech/kurtosis/commit/d3b3de9b6cb7ff0a83488ccbf8756dfb77814c9c)) * improved some descriptions for starlark instructions ([#2168](https://github.com/kurtosis-tech/kurtosis/issues/2168)) ([d7fdbc5](https://github.com/kurtosis-tech/kurtosis/commit/d7fdbc5fa56f81a9194a68f37d1ab9549dc45abd)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 17 +++++++++++++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- .../src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- .../web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 28 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 263046da4f..afc2d08bcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [0.87.0](https://github.com/kurtosis-tech/kurtosis/compare/0.86.25...0.87.0) (2024-02-20) + + +### ⚠ BREAKING CHANGES + +* change registry spec to imagespec ([#2191](https://github.com/kurtosis-tech/kurtosis/issues/2191)) + +### Features + +* change registry spec to imagespec ([#2191](https://github.com/kurtosis-tech/kurtosis/issues/2191)) ([bc20d51](https://github.com/kurtosis-tech/kurtosis/commit/bc20d51fd8fb7d02a8270586dcd4be85564cb415)), closes [#2189](https://github.com/kurtosis-tech/kurtosis/issues/2189) + + +### Bug Fixes + +* change default verbosity to description ([#2173](https://github.com/kurtosis-tech/kurtosis/issues/2173)) ([d3b3de9](https://github.com/kurtosis-tech/kurtosis/commit/d3b3de9b6cb7ff0a83488ccbf8756dfb77814c9c)) +* improved some descriptions for starlark instructions ([#2168](https://github.com/kurtosis-tech/kurtosis/issues/2168)) ([d7fdbc5](https://github.com/kurtosis-tech/kurtosis/commit/d7fdbc5fa56f81a9194a68f37d1ab9549dc45abd)) + ## [0.86.25](https://github.com/kurtosis-tech/kurtosis/compare/0.86.24...0.86.25) (2024-02-16) diff --git a/LICENSE.md b/LICENSE.md index 30f893d163..749a81e948 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.86.25 +Licensed Work: Kurtosis 0.87.0 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-02-16 +Change Date: 2028-02-20 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index e34f7afd25..49ba14ae23 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.86.25" + KurtosisVersion = "0.87.0" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index e6f058ecc6..8787dd9520 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.86.25" +version = "0.87.0" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index 9beff6b9ce..c15e20b076 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.86.25", + "version": "0.87.0", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index d563a0ed50..906fc996f8 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.86.25" +export const KURTOSIS_VERSION: string = "0.87.0" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index fad6d364c2..8f0a2d6d87 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.86.25", + "version": "0.87.0", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index fe6a14d200..f4a41b9666 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.86.25", + "version": "0.87.0", "private": true, "homepage": ".", "dependencies": { @@ -10,7 +10,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.86.25", + "kurtosis-ui-components": "0.87.0", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "react-mentions": "^4.4.10", diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index d2a402f395..8b38eb1bb3 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.86.25", + "version": "0.87.0", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 99bbd775e0..359ee08a7c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.86.25 +0.87.0 From f6cd1c7236c98b53679e214fb1a4a50af6ceab1b Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Tue, 20 Feb 2024 16:25:24 +0000 Subject: [PATCH 081/102] docs: added docs for storespec (#2178) Added docs for store spec closes #2146 --- .../api-reference/starlark-reference/plan.md | 5 ++++ .../starlark-reference/store-spec.md | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 docs/docs/api-reference/starlark-reference/store-spec.md diff --git a/docs/docs/api-reference/starlark-reference/plan.md b/docs/docs/api-reference/starlark-reference/plan.md index 99b570c1ec..c41ec9e5d9 100644 --- a/docs/docs/api-reference/starlark-reference/plan.md +++ b/docs/docs/api-reference/starlark-reference/plan.md @@ -455,6 +455,8 @@ The `run_python` instruction executes a one-time execution task. It runs the Pyt The `files` dictionary argument accepts a key value pair, where `key` is the path where the contents of the artifact will be mounted to and `value` is a [file artifact][files-artifacts-reference] name. +The `store` atrribute expects a list of paths or [`StoreSpec`][store-spec-reference] objects. + The instruction returns a `struct` with [future references][future-references-reference] to the output and exit code of the Python script, alongside with future-reference to the file artifact names that were generated. * `result.output` is a future reference to the output of the command * `result.code` is a future reference to the exit code @@ -530,6 +532,8 @@ The `run_sh` instruction executes a one-time execution task. It runs the bash co The `files` dictionary argument accepts a key value pair, where `key` is the path where the contents of the artifact will be mounted to and `value` is a [file artifact][files-artifacts-reference] name. +The `store` atrribute expects a list of paths or [`StoreSpec`][store-spec-reference] objects. + The instruction returns a `struct` with [future references][future-references-reference] to the output and exit code of the command, alongside with future-reference to the file artifact names that were generated. * `result.output` is a future reference to the output of the command * `result.code` is a future reference to the exit code @@ -725,3 +729,4 @@ plan.print(recipe_result["code"]) [starlark-types-get-http-recipe]: ./get-http-request-recipe.md [service-starlark-reference]: ./service.md [starlark-types-port-spec]: ./port-spec.md +[store-spec-reference]: ./store-spec.md diff --git a/docs/docs/api-reference/starlark-reference/store-spec.md b/docs/docs/api-reference/starlark-reference/store-spec.md new file mode 100644 index 0000000000..25c871cc2d --- /dev/null +++ b/docs/docs/api-reference/starlark-reference/store-spec.md @@ -0,0 +1,26 @@ +--- +title: StoreSpec +sidebar_label: StoreSpec +--- + +The `StoreSpec` is used to configure how to store a file on a [`run_sh`][run-sh-reference] or a [`run_python`][run-python-reference] container as a files artifact + +```python +config = StoreSpec( + # The path on the task container that needs to be stored in a files artifact + # MANDATORY + src = "/foo/bar", + + # The name of the files artifact; needs to be unique in the enclave + # This is optional; if not provided Kurtosis will create a nature themed name + # OPTIONAL + name = "divine-pool" +) +``` + +Note the `StoreSpec` object is provided as a list to the `store` attribute of the `run_sh` and `run_python` instructions. Within +one list the `src` needs to be unique; while the `name` needs to be `unique` for the entire enclave. + + +[run-python-reference]: ./plan.md#run_python +[run-sh-reference]: ./plan.md#run_sh From 82f0d55765504e664754bec386662bdeb63cd26b Mon Sep 17 00:00:00 2001 From: Tedi Mitiku Date: Tue, 20 Feb 2024 14:05:34 -0500 Subject: [PATCH 082/102] docs: image build spec (#2194) ## Description: Adds doc for `ImageBuildSpec` ## Is this change user facing? YES ## References (if applicable): https://github.com/kurtosis-tech/kurtosis/issues/2186 --- .../starlark-reference/image-build-spec.md | 28 +++++++++++++++++++ .../starlark-reference/service-config.md | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 docs/docs/api-reference/starlark-reference/image-build-spec.md diff --git a/docs/docs/api-reference/starlark-reference/image-build-spec.md b/docs/docs/api-reference/starlark-reference/image-build-spec.md new file mode 100644 index 0000000000..131bf9605e --- /dev/null +++ b/docs/docs/api-reference/starlark-reference/image-build-spec.md @@ -0,0 +1,28 @@ +--- +title: Image Build Spec +sidebar_label: Image Build Spec +--- + +Kurtosis starts services based on a provided image definition in the `image` arg of [`ServiceConfig`](./service-config.md). You can provide Kurtosis with a published image to use or alternatively, use `ImageBuildSpec` to instruct Kurtosis to build the Docker image the service will be started from. + +`ImageBuildSpec` can be especially useful when developing on a service that needs to be run in an enclave over and over to test latest changes. Kurtosis leverages the Docker's image caching when building images so images aren't rebuilt everytime. + +```python + image = ImageBuildSpec( + # Name to give built image + # MANDATORY + image_name="kurtosistech/example-datastore-server" + + # Locator to build context within the Kurtosis package + # As of now, Kurtosis expects a Dockerfile at the root of the build context + # MANDATORY + build_context_dir="./server" + + # Stage of image build to target for multi-stage container image + # OPTIONAL + target_stage="" + ) +``` +:::info +Note that `ImageBuildSpec` can only be used in packages and not standalone scripts as it relies on the build context being in the package. +::: \ No newline at end of file diff --git a/docs/docs/api-reference/starlark-reference/service-config.md b/docs/docs/api-reference/starlark-reference/service-config.md index bdc9f9e7fa..2c9aa35378 100644 --- a/docs/docs/api-reference/starlark-reference/service-config.md +++ b/docs/docs/api-reference/starlark-reference/service-config.md @@ -220,7 +220,7 @@ config = ServiceConfig( } ) ``` -Note that `ImageBuildSpec` can only be used in packages and not standalone scripts as it relies on build context in package. +Note that `ImageBuildSpec` can only be used in packages and not standalone scripts as it relies on build context in package. More info on [`ImageBuildSpec`](./image-build-spec.md) here. More info can be found on [locators referring to local resources here][locators] and how to turn your script into a Kurtosis [package][package] here. The `ports` dictionary argument accepts a key value pair, where `key` is a user defined unique port identifier and `value` is a [PortSpec][port-spec] object. From 8a05985fdefea04a628b1085d7a4d7604aa60536 Mon Sep 17 00:00:00 2001 From: Tedi Mitiku Date: Tue, 20 Feb 2024 14:30:48 -0500 Subject: [PATCH 083/102] docs: github docs (#2193) ## Description: Adds a guide on how to run private packages with `kurtosis github login`. Other changes: - Adds docs for the github CLI commands. - Restarts the engine after successful login so user doesn't have to remember to do it - Makes OAuth perms more restrictive - Adds log retention info to service logs bc it was missing ## Is this change user facing? YES ## References: https://github.com/kurtosis-tech/kurtosis/issues/2183 https://github.com/kurtosis-tech/kurtosis/issues/2181 --- cli/cli/commands/github/login/login.go | 35 ++++++++++- cli/cli/helpers/oauth/oauth.go | 5 +- docs/docs/cli-reference/docs.md | 1 + docs/docs/cli-reference/github-login.md | 17 ++++++ docs/docs/cli-reference/github-logout.md | 11 ++++ docs/docs/cli-reference/github-status.md | 11 ++++ docs/docs/cli-reference/github-token.md | 11 ++++ docs/docs/cli-reference/service-logs.md | 6 +- docs/docs/guides/running-private-packages.md | 56 ++++++++++++++++++ .../img/guides/github-engine-restart.jpg | Bin 0 -> 98347 bytes docs/static/img/guides/github-enter-code.jpg | Bin 0 -> 37038 bytes docs/static/img/guides/github-success.jpg | Bin 0 -> 36554 bytes docs/static/img/guides/one-time-code.jpg | Bin 0 -> 26985 bytes go.work.sum | 17 +----- 14 files changed, 151 insertions(+), 19 deletions(-) create mode 100644 docs/docs/cli-reference/github-login.md create mode 100644 docs/docs/cli-reference/github-logout.md create mode 100644 docs/docs/cli-reference/github-status.md create mode 100644 docs/docs/cli-reference/github-token.md create mode 100644 docs/docs/guides/running-private-packages.md create mode 100644 docs/static/img/guides/github-engine-restart.jpg create mode 100644 docs/static/img/guides/github-enter-code.jpg create mode 100644 docs/static/img/guides/github-success.jpg create mode 100644 docs/static/img/guides/one-time-code.jpg diff --git a/cli/cli/commands/github/login/login.go b/cli/cli/commands/github/login/login.go index 3702e405a4..a7dd63ca16 100644 --- a/cli/cli/commands/github/login/login.go +++ b/cli/cli/commands/github/login/login.go @@ -7,10 +7,18 @@ 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/cli/cli/defaults" + "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/engine_manager" "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/github_auth_store" "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/oauth" "github.com/kurtosis-tech/kurtosis/cli/cli/out" "github.com/kurtosis-tech/stacktrace" + "github.com/sirupsen/logrus" +) + +const ( + defaultEngineVersion = "" + restartEngineOnSameVersionIfAnyRunning = true ) var LoginCmd = &lowlevel.LowlevelKurtosisCommand{ @@ -24,7 +32,7 @@ var LoginCmd = &lowlevel.LowlevelKurtosisCommand{ PostValidationAndRunFunc: nil, } -func run(_ context.Context, _ *flags.ParsedFlags, _ *args.ParsedArgs) error { +func run(ctx context.Context, _ *flags.ParsedFlags, _ *args.ParsedArgs) error { githubAuthStore, err := github_auth_store.GetGitHubAuthStore() if err != nil { return stacktrace.Propagate(err, "An error occurred retrieving GitHub auth store.") @@ -46,5 +54,30 @@ func run(_ context.Context, _ *flags.ParsedFlags, _ *args.ParsedArgs) error { return stacktrace.Propagate(err, "An error occurred setting GitHub user: %v", username) } out.PrintOutLn(fmt.Sprintf("Successfully logged in GitHub user: %v", username)) + logrus.Info("Restarting engine for GitHub auth to take effect...") + err = RestartEngineAfterGitHubAuth(ctx) + if err != nil { + return err + } + logrus.Infof("Engine restarted successfully") + return nil +} + +func RestartEngineAfterGitHubAuth(ctx context.Context) error { + engineManager, err := engine_manager.NewEngineManager(ctx) + if err != nil { + return stacktrace.Propagate(err, "An error occurred creating an engine manager.") + } + var engineClientCloseFunc func() error + var restartEngineErr error + _, engineClientCloseFunc, restartEngineErr = engineManager.RestartEngineIdempotently(ctx, defaults.DefaultEngineLogLevel, defaultEngineVersion, restartEngineOnSameVersionIfAnyRunning, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultEnableDebugMode, defaults.DefaultGitHubAuthTokenOverride) + if restartEngineErr != nil { + return stacktrace.Propagate(restartEngineErr, "An error occurred restarting the Kurtosis engine") + } + defer func() { + if err = engineClientCloseFunc(); err != nil { + logrus.Warnf("Error closing the engine client:\n'%v'", err) + } + }() return nil } diff --git a/cli/cli/helpers/oauth/oauth.go b/cli/cli/helpers/oauth/oauth.go index 2a4c940a75..e92f1dad26 100644 --- a/cli/cli/helpers/oauth/oauth.go +++ b/cli/cli/helpers/oauth/oauth.go @@ -40,7 +40,7 @@ type OAuth interface { func AuthFlow() (string, string, error) { httpClient := &http.Client{} // nolint: exhaustruct - minimumScopes := []string{"repo", "read:org", "gist"} + minimumScopes := []string{"repo"} callbackURI := "http://127.0.0.1/callback" flow := &oauth.Flow{ // nolint: exhaustruct @@ -51,6 +51,7 @@ func AuthFlow() (string, string, error) { Scopes: minimumScopes, DisplayCode: func(code, verificationURL string) error { fmt.Fprintf(os.Stdout, "First copy your one-time code: %s\n", code) + fmt.Fprintf(os.Stdout, "Then, press Enter to be directed to a browser window where you'll enter this code.\n") return nil }, BrowseURL: func(authURL string) error { @@ -162,7 +163,7 @@ p {

Successfully authenticated Kurtosis CLI

-

You may now close this tab and return to the terminal.

+

You may now close this tab and return to the terminal. Your Kurtosis Engine will restart for GitHub auth to take effect.

` diff --git a/docs/docs/cli-reference/docs.md b/docs/docs/cli-reference/docs.md index a41e0ee2af..1e7eecc0a2 100644 --- a/docs/docs/cli-reference/docs.md +++ b/docs/docs/cli-reference/docs.md @@ -11,3 +11,4 @@ kurtosis docs ``` where you can learn more about Kurtosis' architecture, APIs, Starlark, and much more. + \ No newline at end of file diff --git a/docs/docs/cli-reference/github-login.md b/docs/docs/cli-reference/github-login.md new file mode 100644 index 0000000000..9f3bac5476 --- /dev/null +++ b/docs/docs/cli-reference/github-login.md @@ -0,0 +1,17 @@ +--- +title: github login +sidebar_label: github login +slug: /github-login +--- + +To authorize Kurtosis CLI to GitHub, use: + +```console +kurtosis github login +``` + +This enables use of any private GitHub assets that a user has access via a [locator](../advanced-concepts/locators.md) in operations like `kurtosis run`, `import_module`, or `upload_files`. To see an application of this, follow this [guide](../guides/running-private-packages.md) to learn how to run a private package. + +The command will output a one time code. Copy the code and press enter to open a GitHub window that will instruct you to enter the code. After entering the code, authorize Kurtosis CLI and navigate back to the terminal. Your Kurtosis engine will restart automatically so GitHub auth takes effect. + +Under the hood, GitHub will provide Kurtosis CLI with a restricted OAuth token that authorizes Kurtosis CLI to perform GitHub operations on your behalf, such as reading a private repository. Kurtosis follows the same pattern as [GitHub CLI](https://cli.github.com/manual/gh_auth_login) and stores the token in secure system credential storage. If a system credential store is not detected, the token is stored in a plain text file in the Kurtosis config directory at `kurtosis config path`. \ No newline at end of file diff --git a/docs/docs/cli-reference/github-logout.md b/docs/docs/cli-reference/github-logout.md new file mode 100644 index 0000000000..9c18ae48dc --- /dev/null +++ b/docs/docs/cli-reference/github-logout.md @@ -0,0 +1,11 @@ +--- +title: github logout +sidebar_label: github logout +slug: /github-logout +--- + +Logs out a GitHub user by removing the OAuth token for authorizing GitHub operations from secure system credential storage or plain text file, depending on where it was stored initially. + +```console +kurtosis github logout +``` \ No newline at end of file diff --git a/docs/docs/cli-reference/github-status.md b/docs/docs/cli-reference/github-status.md new file mode 100644 index 0000000000..c896290522 --- /dev/null +++ b/docs/docs/cli-reference/github-status.md @@ -0,0 +1,11 @@ +--- +title: github status +sidebar_label: github status +slug: /github-status +--- + +Displays the GitHub user currently logged in, if one exists. + +```console +kurtosis github logout +``` \ No newline at end of file diff --git a/docs/docs/cli-reference/github-token.md b/docs/docs/cli-reference/github-token.md new file mode 100644 index 0000000000..2f0514c229 --- /dev/null +++ b/docs/docs/cli-reference/github-token.md @@ -0,0 +1,11 @@ +--- +title: github token +sidebar_label: github token +slug: /github-token +--- + +Retrieves the OAuth token Kurtosis CLI is using to perform GitHub operations on a users behalf, if a user is logged in. + +```console +kurtosis github token +``` \ No newline at end of file diff --git a/docs/docs/cli-reference/service-logs.md b/docs/docs/cli-reference/service-logs.md index 68a2554d29..cc819d6f55 100644 --- a/docs/docs/cli-reference/service-logs.md +++ b/docs/docs/cli-reference/service-logs.md @@ -6,14 +6,18 @@ slug: /service-logs To print the logs for a service, run: + ```bash kurtosis service logs $THE_ENCLAVE_IDENTIFIER $THE_SERVICE_IDENTIFIER ``` - where `$THE_ENCLAVE_IDENTIFIER` and the `$THE_SERVICE_IDENTIFIER` are [resource identifiers](../advanced-concepts/resource-identifier.md) for the enclave and service, respectively. The service identifier (name or UUID) is printed upon inspecting an enclave. +::: :::note Number of log lines By default, logs printed in the terminal from this command are truncated at the most recent 200 log lines. For a stream of logs, we recommend the `-f` flag. For all the logs use the `-a` flag and for a snapshot of the logs at a given point in time (e.g. after a change), we recommend the [`kurtosis dump`](./dump.md). + +:::note Log Retention +Kurtosis will keep logs for up to 4 weeks before removing them to prevent logs from taking up to much storage. If you'd like to remove logs before the retention period, `kurtosis enclave rm` will remove any logs associated for service in the enclave and `kurtosis clean` will remove logs for all services in stopped enclaves. ::: The following optional arguments can be used: diff --git a/docs/docs/guides/running-private-packages.md b/docs/docs/guides/running-private-packages.md new file mode 100644 index 0000000000..0165cd86a5 --- /dev/null +++ b/docs/docs/guides/running-private-packages.md @@ -0,0 +1,56 @@ +--- +title: Running Private Packages with GitHub Login +sidebar_label: Running Private Packages +slug: /private-packages +sidebar_position: 14 +--- + +Kurtosis CLI supports the ability to run private packages hosted on GitHub via `kurtosis github login`. This guide assumes that you have [Kurtosis installed](../get-started/installing-the-cli.md) and a package hosted GitHub that is private. + +:::warn +GitHub Login is not yet supported over Kubernetes backend. Please create an [issue](https://github.com/kurtosis-tech/kurtosis/issues) to request this feature! +::: + +### 1. Authorize Kurtosis CLI OAuth Application + +Using Kurtosis CLI, run: +```bash +kurtosis github login +``` + +The following prompt should be displayed. After copying the one time code, press enter. + +![github-one-time-code](../../static/img/guides/one-time-code.jpg) + +A GitHub screen should pop up in your browser instructing you to enter the one-time code you just copied in your terminal. + +![github-enter-code](../../static/img/guides/github-enter-code.jpg) + +After entering the code, GitHub will prompt you to authorize Kurtosis CLI. Kurtosis CLI requests [`repo`](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps#available-scopes) access to repositories for the ability to read and pull from private repositories. + +:::info Private Packages within GitHub Orgs +If the private package you'd like to develop on lives in a GitHub org, the organization must also authorize Kurtosis CLI. To have an organization you are a part of authorize Kurtosis CLI, click the `Request` button next to the organization name on the authorization page, then notify your org admins to accept the request via email. +::: + + +Once you have authorized Kurtosis CLI, you'll be redirected to a success page. + +![github-success](../../static/img/guides/github-success.jpg) + +Now, navigate back to your terminal. Your Kurtosis engine will automatically restart for GitHub auth to take effect. + +![github-engine-restart](../../static/img/guides/github-engine-restart.jpg) + + +:::info OAuth Token storage +Behind the scenes, authorizing Kurtosis CLI means GitHub will generate an OAuth token that Kurtosis CLI can use to perform GitHub operations on your behalf. Following GitHub CLI, we attempt to securely store this token in system credential storage. If system credential store is unavailable, we store it in a plain text file in your Kurtosis config directory at `kurtosis config path`. +::: + +### Run a Private Package + +Now, run a private package! Get the locator of the private package you have access to that you'd like to run that. For this example, we'll assume there's a private package at `github.com/tedim52/my-private-package` +``` +kurtosis run github.com/private-author/my-private-package +``` + +Now, the package should run! Additionally, any [locators](../advanced-concepts/locators.md) (e.g. in `upload_files` or `import_module`) that refer to resources in private GitHub repositories you have access to, are also authorized. \ No newline at end of file diff --git a/docs/static/img/guides/github-engine-restart.jpg b/docs/static/img/guides/github-engine-restart.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6e464bb9bbf231937eb64ca2995199d5dcd28a63 GIT binary patch literal 98347 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 literal 0 HcmV?d00001 diff --git a/docs/static/img/guides/github-enter-code.jpg b/docs/static/img/guides/github-enter-code.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b040eec811d366bb1b6644dde141aacffa981c70 GIT binary patch literal 37038 zcmeEuby$>J*EcB$5-Nz2BZ$(V2ugzpsEBkoNXZP{DWD)AU=Y%X(j7BH4@h@+O4rbx z-yS^YIp=!6zrH`;?|Qhd3GVyeYwx}G+ADr*?Fm$PB|}0)O@xJoMe_XFQza}coCfex zBD@5yR43w2fe&nZC7CB!xgE4i;2$FsjpwFva#(EOJ0TW!fCUyF<`M9s1}`iuyf6M( z1mN=;cqt@dW8s2NY|Q;6oWGyMX-LBT&v(!x7M6sv+@q{4wj=9Xu z2)TH~!BT`#LrwuAX>Df$;pgJv;$ajefBo96-dg9n`82~K-gD+fatPAhw+KSchf^VG!N*v`Vn!NS@Kf}v|@ zWbNo6!pMl}=s$md#%bbW@lQ`y_J2(aOpqIMhx=b?1M!RJAv;leC6`E*(VwS((2Y|NG`& z4TZTeGyfMS{;>JtD&Vsykudjv)=ZSBjf|McS*>*33tNp`+6`N+?h)^_b2Y0PFm+s znrYs%NGabQUlt1VNUIzlj_!=xEH6bCxzd&Iu<;V%5JKK!{dptNfuMZ$6W5sjzCZ{e zCnUeQEiI0PjYt0Hts0;3qbtpSnh-*!aPS%`NdCHudCmqK$9Uq>KU4`zuzc=YJ3XauAT8(F`wyLR(B@gkBLwPN`li$POy8{Eo7Ru}_8fS@PCasafUt(~a+`^Jq!)AhlyiHgI>s3`SwX+>O&AdBDesR-3|i;xoZjlYVK)0aLr z>l2mP%4x3Xe+SB7aW1;oJU^?5`&+L^69VGjc&WWchXUDLS;w8N1rFW@Tc;x0?VC#{%9V`js z5cWwYx$mzHAgvk7_l{O9i`+-{OVI<`=y0QwvD2ine8z4-cskIVKXwTrpK!2sRUW+_g{?NkTD_8hWmN^y zSeMkSRW9gp^=R)(N)VHaLmB~jduX*e7Y^Z1wIumOQKxm!^WsQO?Nr3Hs?KPffIW0w zWUn~7$TVj<(#8$265@x?LMl`qQbrkP0(zGM~a8C870`?bSH@&JdY^%%%D@_f^9jZ6rco?658}B3z~D z9yey?2tU23aR=*79WHKbTTr=^7-cLVt1~Xn1~)?LI_-|ib!J&T(v!qIQH+&A@n^Y6 zWW^8b)~n9-{D_12T3+NROpYF`-p9u%*`zms<(axeL0CBVuXl~qwcN8>lyYq>Ou7)@ zYz}dQio(mkVr&{v~UH55!wvwl{zwc*uio`jKYnZLcmKb&&Yi zh7iC0d(y1vh?uw3YLQ=slK%Mpo_7H{k3r{l@EZ14siw9WNcY}s%%3~;1Q z>23An$l4ca>2v&@|@elOP}@k%Td6!&5|)ePd~u(voLjlYiY`(4+q z?#|${QBx-ecgroGf$^E=&&|#Czwvf@0;o*i6=#zglMX+FbiKt76+B88#INW3{JyBU zAHS2Y|8vkFC3)VQZ1(tSShKH7d|KIHre(lZNC324&ar%h8Y4KCfP=;&6%M36{!CrT zQo#m@l3WqBYRyrl-YbPTLv_i+Tq1#t2ryRhAEO@{5FESFYWpjZ}8$a{crn2#bt(B9S9w*sE2S5seV4Wz;~ zEwnH6*b2P2db}=UvQ->MjFF)^oY|XsTep+20W0-=csm8N^i<#Zm{+nj7y4z%PNvH# z=asINjjT3^VmipiY7g-yDXYH;a)nYoIfDQgu-03p; zO|wClE^0jT4=gqGS3m{RZ&$N4#4hD%Akn=pU%S@*m&3QNdKEP~W;R6|=oaB;Hn+ z-2V8$^C`q12R(|Ewa8QAtBvWA?k{+-%N%jKH0d2Lh<8cGD(_pc36%nJWhebs`^iF^Lbn1QIU)W||C#!v2i=+0 zdLs+cFL%HBfrf;q6|rt(YZ9- zF)1cU$)&3$9 zu(%&0uzYN@{l9iVx_I!8cAmFiGs=ilY*#x}*|iOzb7~sk>fypDP7}Z_)*n9^@yMIr zz1<2{wp9-E=h3DOm0>dtG3h9TeP;;+!mbWfRY@qv=@UpQPJMm5a!=9YoF|Icn`Ek9 zNU^=hTvhJAv5c{WJz#Kgq^iqLv3!azKeQTq%yFn&J>Ks zHs~iJznQ1y-4Tj~a~1z+Z;58}L#DI5YQBaedKk$^xkQnpg=nb??FJ(ghrGv(vhF7v z9{Dp=_1h5s?EUA|S_(SW`CCLm+7xM-Y|9d$_d<+1Vh?yP4fNgvAAZ=#9rr-A#f>3{ zqV^t-OPLt$p_YT?vzfWOunH!nm?RZ^<7$|}zRG2TM7HOEgb$Pwto6X>@48^EQ^bo* z+cU@LO^hX9raY^!_t%D1I#QVKT`cn}(9?qd$4g9I`9QJ4RckM4ysxwdCYw!I2$(#; z32wj%FXIj%Y^)S4EG+5HFrei$)&=cye0^9K?b%lC+e7w%Ev*IOWu-AAXMN#JTzJ=C zfGd8_$B-9?@O4^sp-F3Dk)h^9rb^ov4(Ef^pz9{k$}@Oezgp^*`JkD!l z;SZC)w^OGjqcRHRo3WHGWA@S|uCid$2T!u!0bf8_@T#yfXNZn6{XuscFQXG!K@Z9W7q{uhVsMXZmaIGT3m_1>^}=~ z2+u2AHdQeHBRtd2==d)!G)e|>*Ld&+~C%WtBJG+k|7S+23(1LwD1uu9+{8p_Wa(8<^lP39N-~lx~37j zn-4sGRa4+QO+fF#<15}?o;p%7&s#`twt!p5W2{5)kQ9iyD)&b3!6vKq zJJV1~jd{E0jcxm=HI!J?b8{ikmx3i&buHxrCLn)bt3O<2>g}OTf&64Dc`0jrolP}X zCWJ0Fvf2v>`(?9fR?vW4A2c`!6M3nVMYmCh^;CNK#2u)l)YL6O4Aa=L2maRisnrXK zo~FlWls7znaMe<7Hm$vNJ-RoD`7^JB_~C)1p5~WqPb;H!Z|!V+#7dhf(`0AXDKpjYDwvuS#-*7LT!h9 z$`JR2tGL3_NQLU9!|0WxBn2)3P0{3vZME@vTk;tJ*s~Na+aXA3N1VX#yw41dwh5+R ze}reMm4C4F;71=ftmMUBx!8h|lm-nY-$R8@rGddNqnQx0Mi~>(x)_91%JN%gi>r%i z%_Yl{unY%-CFLA}eD zt#2(~O}nsC>cA6TrK8dE$HLhqciD?JlM^W7i)bmT;X^YZ2TdIMDb*Wtw0A&og;A=! z4UR@L5WqEjpAoL#k?47-K+}ACY!r!Z3JI80H5-*dgG&S%HVN1t4woOk z>1#rayTwvxiMbBtt9qrCQqx5H)4i5dh`Xeb3CwdG#?lRc|M45_cN!lMN)1a8&??O2 z5`|)%<_NChtj%iLaf!Qy-!HOf7n);;5u1S`JA8d7~pntsy5CXb^9a!+Z>Kbk^ zadP~vt*zCZN0X$!&5cYPOOY_OC>;mIE`nVt&*Y1NKD`bU$$b3;W(YnQ1cEY3-*A-V zmJxT=#*>a6hn}C$Yvf)gxXx=TGyI9n9IqSD&6JheQUD{5<%58Y4zZ@i838vsrWl!J zM>o^zPlmk8ByCd^`twIEF-GJcf@tN3YXJzv9zuL>@tTfo{EoV`bQwABw(>$`aD1~z z*Q#_WHR6@+{8AbL%x$YVjW9^P#}I>fC^&<*D^`u4!DOoOT3TANJ&&Cb(UaH8WEVOj z*G44*Dg^Zht;Sc=HfKEjUG5y#-o65S#<$`@z#=MNdEp4!@!(P1iHeNWun0U4kGv$7 zvFcitCq#R4ASbZ3O>^~1+O?=MjI`Xr!GDe@x&_unJc$}R=cp@PQDWoAI0Z{o1Si?2 z))!G?`c*j`=~A87WlP??qmMeDJ(hdCYzE`7)XS9D||y)L5uKT8Ch zw9vZxj*G>;c(cW5|COd5?!W0^`8)%1`MG51V)z%tAi3a*EuH4kd(ia-9SHezVE3<4 z3H<}Sf+1Bu&SjEC{WTuYk`Y8uGSfz8|Bx;L&T^yU>m}e^{GkJ2D4>0w>8yVIhxC7( z^nYe@2gC}2!GCb73;NS+;L70V6#c*HVBvfQaUGLw0q{2eCar_^Z~nC> z{CcRQ!|=Vx2*35r;hpde7kHF zQ{HSiPyshc)g>f+;8wj2ksNx^-qf=Yt2 z_85U)fKo=%h5Vp&1ej+oukI14ih{6n$LbWf=26F!_P@56f;zUG2ItPO(dptO?DXX^ z8d;{+XsgL69%>tT<>}Cs6nXPhjYMIL=Y!eapH6X(9%-LDtxVdyumXn2Z1{jR&81^i zQ*(eV#_ul$(0_Sx((07vRWl4I1`ZlAU2|X({?7T#hQ2*R51@XBUqG>io5~Nws76eN zle%dCtnVB0F4TB7L={(wwBn^f9IoHf>;HtK{|aUQ9uO{2Yyv#b9$x)hKK^Z$FUAbs z1Rx*;{x7NjMZCga62mtGb{I-wK3)cg6?aRk@`L<`E5FmKM(llK>4-ctJLBF|IrMiK z2DqMOwqd96_D5Zh-|wS^J%8GRh(p_QAUisza&Kg>W7K-Ik-?jG>ELLsqUI9KP&;zs zja=-*f~?$%o&?}v9H~Y3%$%>sSVE(8o#E@K^9fSXDJ*tY*5?+dD!?@+>{I^}B<|+C zn^C^kRVqtG^T^a;neFFYyf1{bo^AJ?_DUD-x;h@t_w*er~VBg^{r;jPzc@y>|Y^UW~In6FC+(bF+|M2FxC9_*W#cNJ{b z#|{xL>UlI2e10e|WuIF%{i1w5abNAE<2v3nq^o>AhTmjs_OU}s&7O}+&?TWA2U?#P z{H^Wn9Pjfpr_p%l5xGZ`Mn@vOa|^7>Y_Bu{JWX^^SU~Hz)6LGuem_Znsa_EJc&Zu~ zeSF{8H*sA$OwU5t^kt%+;!38C+y3c3`-t;Q`y=&YDS6ZO7!zt#{>jPq`V6(z3}&Bc z^y5UIt6Qn=f7@nyD;|L(c79SM7xS=rN26bTOlZ9%+MBq$mIM zs5P1+QA9091`*-wi!*9a{Wc;7zOq)HSDYhC6XT-w9O%Lg<^F(h`?D>oPepy-{?;yWts1r&%hXJq##`J{eaK zZ3^(=4By!Z5ZS+Cy8DZNSKnDzXdJgY2QeWy_L?Y^fQ%lxUUh6$k7$t(3JQWYe_@Vz zG`O3@F;G)ANX*8;hqp#@%I?|aAQ&7rYS`N+mc{36*(WuYSm3O?aODlx zp3C?8=2brLKY!l~@F=UJ47rTX!{rr?*u4TW;NK|&XDVUlMIL!N@~x?Pn7}ei`MD1P zbej{Ao^Cy~l$J+vb6vPe}ocek$E^dEPqB}vsWiBR|8FY*i^ zt=Z;Ao%Do?ZZ%E09t4=)n%oE;_%+-$Ds1KqpeU!g$+H`)$yiQPcVpj`6NC{7hl!lI zjGE7cz;^r9$q?-cBBRoW_$4+$D4O(oLZVG^VGfU#x}hXbWS7P16uZSy&v;e z@46Styg1%Nqa)5wJEJDPe<5Ub|#J!5w;F5;aVJhAm^V zldkBLpQhJWf3jA-Mzq}Z-Y)IX#d*1p)jU?|_LEU%d4YP4fu2eo~Aqm~4pz<`h$Z^510uvc>?f4=E^LRBM zQhnJkVRt~=8b72~q@;2x&s*0}+tAREM>#V|Rbp4-c%7j_bll#+{b>0bo3>S{3HhD( z5=98s00c1MkdziWzlfA384;ZbL(KX+z>y;;{ftARG#bnTx9M12pZg-AuHqz5% zlV7dMHKuo6Euail%uh9N60=U@N*?XNEd{}se}KyIP_h*9N`IDmy=a*0a-)CMNWikv zuxS%NWuB^+-G1eME)P@Qpb64{(D@|s92yHlMAI!7b<24~1ZG}|d1Otmk`e5r>bA3} zSfIbZHtq*=yg%rpei-oCbAOaOo#{lfX!AQd_6&QT=UjXP?oHlR%7&Cr9Z5xGE#f|# z9s=PUe>Okif&LyD4xNxw_E73QL9ZeoT))Jsvzp(T?78VKl>r2uzXb+&_h)g*L@0*d z3$c)^d!4LUSDM5gxp3%JJoX60gx`=1EP;mS7A_!R_{679QypLUciU=5`D)=4g=~nr zYYQC`S8D3^%KXXI{1Yul;@~) z+b8v^e0Y0bJqrSzU%E0%cFPENKz~wiJMXNvaORaz^Z?TUqOO`RPL*U|S^P%idBw?b zkKS)YT_ecryS$$Q!fI8p>%s5hnaC{Mi~2$q?5l>a%Jzbfu=gs(Qwx5l{7xO~s-;w? z-}qGT0`Me865Q8yH{BiOui-xPbj&-MXYjg7^^^QvBi%v zGcD08b4b^ciJelsC_(*v@>~@_CqDy@ig|5=AuSQh!)4RD+vqZkRRR6jAsK5_fcD03$zoY_wBU-|Ag-@Tj_8YpknB~`Q0WIJ!@8>|? z{wYAJ#>z-wYonH(%cHN*^qw1pQwXkmx6+OC9P-8GW%z<(d*1#qG$he$Zw!W{Y}iG- zvsFy|MfO~A5HyC^)Rp`}os=kJJrAwpT76>G@DIa2mD-dN*vTVyGng96cYvA;i>MueXX_b@^vV2CmpFW;Z6)!b>;~l zqH2P)m7gB#bYU(E?Sx9QDn}!T`7P&FsAQM8+*`OwgSotV>uvfYHW)?G+iODW*~Kfr zFH_aM=SFPjjnyVcx4>FQuUg5r`KSW-h}kyYs9jtPOqMcW%za-3v7py#7EjrAYMAKB zw)QmY<`p&(3D>efDf*bhm#Tqpa<^AxTt^zu5Ajxz9U~H=*7gi}h&d|C)J)jQF_PG@ z`+a2KB#ZfRFn5K4x9m68NsF|2bcc`?8QEoe?`?<%wyjaOE`c+pO-m*yrzXf?;zYAgls+777ET*v+0LRp+Ag;iqzB(!*BTaFLiB^C6W zGv>NkF1$suOjgBS*I+;2s#Z7Q!PkyG$Cux1u+?k#GB^6|-h6RD zBj>Xwdotwa2h4T`5vk5s@;s)lYJ7*^F}^MN99ktjO^I|f&IYm^Ehhch7DkGhK2@aZ ziX9djdb^Z_d&AC?Q|c*{W45EPgvf3|kKr{oeXeXH$&`CDB0TaOW#oCO4{FRxuLm#R z)XpH&GJ<{ImzU`023hCSTXT_@GwuXBs_*J#Ftik@of2UkM|2VfFpy^o-i;Vd{$|-v zn)C7M4g6HGvkgg`&ql9$jU#%7NS|gaDkDr z0e&lge?~`Gry{N|A+|pq%&s$gV?L{D^Js;A=O{APs_=7v+qb)G*}H+Y)f$$w^zl5$ z!e=u2zmxnmRZMc=Zwclseoqd@j=&W%Zul4^+20@o|Ec5J zv6~qBIC&aZy8hMG#J+(FJNk8@DvsEJBGOic&bi91%&cGfE2jsQd5=U&JYXxCqEqH3 zp>@^-*SMFn_nR{?*g^W8Px?#FUYL}rFP~?G*fCK5__A^D@3h<|U4N2BjJ}5YRW*S- zQ>TKD@2u~+9TKvoqk4LW3!ZYTcVn6bU&MRIAt-;5v1BCt{txfO0yQqEi)~_Pe<4)s z@)Ms(uV2+KsFvQ(8K9cjb)2}yQcY@q?Y&_wS$;8n4I1M)Ajjb};&n{v#lJ-pXznk< zaU8Xvs7|^Tj`*aJTe(&S36X#e9<`tjU}dT=*yp%eMqq64Xe0Jhu0aAS~c8h=@%@guExdUG!*5GI0q?ftP8h zm%i+|IC)M=MogdRz9t~lvi>6V+NHjc^+~U^$`Oj3&?K7I?J_f(P0xiQ}d zt@fCg^|r~l$;1wlSi?$Em1yjBvC=B^#zkJu-Ars};4u*A`B6ES#K~^DlqDFo_Ks>E z<_vQEB}@Kl1l`EX%|TnaaA6%~RKENii9_U1*`!8EVR?7sOE6dAhFXmwl52lhWG>fViVB9VSm8p{h6<_aaHI?t>$@CJ z*-VzNl`wf|Z61s}Pv#V}Nz?~NB$zV?ZFdNy8D;s7Pk0r4^KIwp8VGhC^K!2zqOz_$ zOw|pyi4w*4);>6@&{BSUpG6HTmB#T7dn2v$7!kLHYyRswpBH&_>&Pc+!;PKMC0qVV zT>P86;n)3)ILDp*C*KHhl!Z7=Uvn8zXJv)!+Lt2cwJtMq#K8MMp5#_RW0lmebuz47 z7iAM+E?DgF>~H6>)ZewWMuotAH}e-sU1$!VZsd&FLJtWtY-ldjK_&(==X~Kw zIQv2Td2^66Vb&NFUp!{G4>}7W`$+%lfuCDpnvU?=Vg3lxPO+@+GW}CEKH}`Sk(MlS z6;ei3{pTq{(aZ^wHCY3tsv?2uTKDtiFuc-{~pViF0d+~^!RrV%B z%J9j0zj_u)LM=b@h~)|C&Zzu~pJH;vp6b*|(B!Jr3dOz1x|uAuusaBmf{;MR4O)!u z;P^CHAe$F)zM|>M?bZ2Be1S_BI`*ymmJq|eCy)r7-&eA6gi^3xyESu+^E_oG1-b8T zA|!WiZkV7nyS2IWgH4>0<U3}P*-U8)!LFPKN2+|HUe?2~Iww}7))LQA*Qcd@1tTGsP;nKX3D3cY zo*JpMxd0)I450&WB!ul3xg_zVNC1n$-?dDSKTBzvuK8~97hDFguxOCu>4@Vq`v*~N zGQ!|=^t3S;tm7}7PXJtM)3m?)2b2QO`|(v9)D=d#uKDi|F=v-p+es zQ9Hf1W1}I@hx3=UmLRilG*~W-mK8^}0@UZkR9g)`9A0xTn)(%b7=?C_*}n3t>>> z3kfkAF1!{ie5hW&#IQe{J!}sfBNH2z3==jUFLNpYklCeUiW`(#c?r7EQ3)E&AHE*3 z4^8|DT3eMl?S|hYFgZ8A%f+z44%Gcv?Ov;jgnFEdqYMVB_-vlre7p1*)D9Ed#Uy|5lJYM|D4QuJ>%XJW0h`zThG$k6S#vPCMoL-eU9H1(cuQ6JjNhiyEXi$$4#7JJ05NWN-8!F={(=Q8T3;~aG&!&_Z*4OYd6O8U&H2@*D3TIR-$ zAxHsH$?VfskI5VL?dT>+C*qD2d!;|wohE*uQK2+fNBqS7@xz}K>|1FMQ;!R|ldqgV z-*9;(CN_ELipHRsTd-%l5gGK+Ioa2s zeTI~7RC^Qw^Ap_mjW#Zt5Gfy-C?_RSt7x)$BtZ8^kMn_C?clRIQo=fQ@fC{w#ew0y zA=edyVZQqM6cc8NPvu8%=%7=kxNY}7`5Bb<^)eHkzz0HTG#5+{5n8<-mi^|@lnYTB zpCi6mlX5j}e;7XZb9hMpZaewHG1T_EX~=1)(@}R2MT~^id=aXJ+3F|pZcwb|XX{@1rD^;~ z{b{Pj2rl?E0-e3CwKBAvApKouMhQhI&q@J+_9EY3jENCevt}Bi?%A}a@!GIws+!J~ zyHMn~Gx7$fR<=S4V%({bjvd;%!%d~(} z-l$>r$xx&->TF^Jv2I}LpxJH~OQWgx;|WgGv$-|wjgt> zQS7Wc^zl|xre4b;zkr_oxTA!DX5w{gR4u6l9j1U*q5TpKbc+3Rak1CwQGcmc$a;E# zvHHGF>$sgaVL9+{u@qeDi^Lz5BHhMec10K z%VVg0ReQ|C`m!Ua!AivwZ9#8;$Muos7p85y(WaduT=G8-M06QDd)YmjACZN^t?WFb zU@Kh(KXg6O@S6Bke|@_^xtPb@q#Fi{8%&}3+MbaI=46k__N$95kyd*vc_`C4$Xdlr zs60VJga_HB*t6OXNdR1)U{3^VW9wD8lx0|WFo|1A#Qiq)MDt}Vm;IfgG zjHO|mn}VzFwZxnlzh>ZfiL6kT8#OlJF%yzacT6c*3u=izQ389cdH>2o9G^B~mLZKw zVIQV2MUH>3$isfeX(CJ$ zMfu;fK#!M^Df~m~NEY3a>c|5U(K3ohYUyZ4m#&-&Bl-BQEvpvoz4|F%;hBClODeq1 z-K9hRQRsDAuWh+U_}HJkN_+gigVQlTr==Oq;yn=4|;kaH*cWhQkTH`O<^h9Px$_}8x|u^_ppyUakI;5i>{pbi`4u*p)( z@hzuhdq(TD)l4ZcY3Lbtc;)zLC6D36ZK{TVKo7UYTV3CI95!fhy0p}rHtKnPk~SXk zDaH=5?|F_(N*FoSJ2D8WEbTR58!m&D`&wzQ;-T{L7SW=xn9B2R8GiQxj^ov$PAI{- zLXtHZ{q`+A{7Nc4htGU_Dd!Fek{MjVKs$l#4Jfz`dpA zdYBkJ)N3XOnQb4%g(xSO-=DCTFJZrk5#cyLl~h`5gE97ns)lbr){13aT_WFix@YQ@ z6Y~)#p^^8=5wUif!=Y ztZes{)PeE&;ngM7VJ1bZuz}{Z#PAmLRJfL+!qi~{J;MsA`x{BmARG}&eCvyps@JBs zzH18OW0j;#EVUH-g$}0`H1fFK5ABP!EJM;&u6<7|R?JOn5G`zgmQdN>gBo zxTD#<`Q}Fuy*B&}?1hX=pI?1Jf@o41so?Af2BKL5X(~;v{p{zLbW4Ng>}|w5zmH?1`Ko3S$XyKoG&Hv z#yXB#^`YjjZ!@DueuN3!eHPJWE8@08Xc~WAKFPSxI|ae=Y0zF1UA*0aXcGHoK3Wq% zPwm0d)o$3Qr+t)Xe{xtd109DC<`M?+Mbfn@n~5Ip z^x8bSfQqO=sBd}4hyZVzU17fHd(2hNZ9HSnLBvTT_ZN0$z4zRMh^4y&FDKbumnEar z+M%_%uxy!aC3ZH=MXC-;4P`PXew{2N`hkt7rMiX@XDlDQA;C{^e(74yh$Ert{n~I7 z{5LJ!9s86#;bn@`5-y&t4-NQ+l9=_hc1o3RtZL@LyF=5}`I z1kUb_^va9Cd#a9Jc-`xi9k?=^NGc}mavC-l)QNvL)!z(yMk{7s?EZFiwR=At|!_a`7ZvpdT2aG(j@Q!-x({>CH5edY>XZ_IipPDK{(0DbR+EK7t-X+FrD8d-P@pxOtk$P=-1}gL!shr z_%B2@;(47VcqFOD>S~o~sSn>f#c=LL`Mz~+C*dzn38sn$qhgf*Y`$7lLHvSdc;|fz zzg2?~jTkJnT~}vXOg6{XmPUJ8pq`o7RJ*)t1+S=!@rn|g%6F>M4DDCP(%Sh3-$6B_ zh)Y4Jw|T0A2w4opO|O&9#+vLE5G_r(Y`3%bKdv_Or7ZDW)1L{L1BpxGL^a`8n>&Ol zUg;lc1s>!t!lm;J5XwcJAdiKJ5@zY|luKwyD(r5c(Y~4fUVp)|yocA9g7Q=;IGK#i za4p*@QP{Q6@mXQApYJe2lqoBMAsokUm{J|jtZV#A6zgZsf?>^FJu%;YkK)q!W&OR2uk=U=ZITXO_DpGW|^=0 z%A6bgNXAl^f@u`n7?MwsZcLS<^y|`S%{{Rmqj!4!Jg=R*^eMBt?X4~$nG%fyj-bGz zfH;H^)FnyN~BynxWDXMV;#Ts=7ZS# z&fnW(P+cR4O~>HinBh1j`H&3=bi|i=Z#xe)^l_;-J3BubzKW10*I*;;clFh@5DTP_ zFwZ`@iOp*y31RQ?cFr`o80taNC-A~ua>tX!$mfb@x<4T0=O$v}o zuI7K)tbj6gE0Z|jt@j(07tXiTN+>Mo>P*!5HFF)w?9TP0E?M*R^&A6o+%)Qdz!!e% z=d%^;xM^rKJkd)soBR+jM{sj;P*1i@P{<%z88 zP5K~)zkdrLK#G-;FXCy~hO3K@>D<|hq`&D8vW;7A?Xy`r+xsYju(EXSf<@e4M^!}r zC;NM2MxHzu8FfXFP55D6z>RbNnu5(E+b@&VwJ59o#mC?3RXf(V$|pJHox7H=n(6I6 zT3N4|UzK9sTZxn6f3vI;%V$=QGg0!MPdn23YlD1Yr%dRk=A1{Yn04sxkkO|(K1+JM zF(Nk#;U#uY{P7xSF{2e(h4+btT@K1gzTr$ArLkgvo~gt86QrE%XBxxg2@h)3+rpKz z_wJ->#VIDxZyf72C$jRW*zBdGLCyIeoOBPKlZowosyv@?uc>L7BauCK9Lynv)WR~{ z#zweB``YrA$SEFAfGQSBRrWMw%EE}=@54b!;W>i<7?MQv1FdqxnL>ea%Dwuz6>)Zy zz=8OTu}gYd;Oe|Ujr#$Q zaxP zIw|6d>?boKX8TQIbSB4c`EHY_GgX-9uB#$1T+7vPLD%WB={xSNBt8ys7|lHI5dW>9 z_axuo6<_I)nlSoj>Z%Udi9uE$>`g9lBZ+NUtWUfNrMvUU=fy6x4NM@Bus7j-8l;(QEF(bP|mH%2a^Lu z`_z$xKTG9yRf436exBw!|L{OqO&9C?F$p=Y8f-@$jITSyn(Mnh!G^2-?E3iQ0d{dh z!7`B*$1mB)fcWU4H8c9iesK zZ9j-zoPK(m<~)xHWQg^5Slyl~TwZnbSFNLMk}o*jUdr5bTTL(^DAcu)yHI&56YCKF ze9ER?RehMOYBrjyD=Tvhoh9BDC%}8qmAv~=HPoRtuhndJf6Ouug493+loz!0)89eu zuesM8hCA!d9#$K!f95u>r z{7=8bL^%{OCT)ah)^W8bYuOwBjW+Iaii&uV*i1vVaBoDoe zp;$rVvPtrveMi~7lY&P<*s_rv#65R>hs{vi@xRbL*W?i!w~X;8&fQle%a8Rt@dUCm zsIRmKy-_8^4+s><3*22nbjK(Tlp%gY7f7luZRMWzms=HoPs&3`VP~j$b=4b3*nd$C zh@|b^GLCMtN6*W~SiBE?pKz|Ns`x3rpWzqnc;?<^qy@kJOsYz3etO)rMfMEm=i7OX)%hd@s;yW(~oCF-$> z(mu2I+Wau1alvB5?%=A)N^W=imNo)$@@hOsGJs!#$lo+<+rt%cjVuUMK{<1hIPcXz zIz+oS;#tm{e$l`lyO;1FDeu_vdZ3Yft$9ze1y5^fSmy@}hUIev4l@kOa0`XP`YKFf zWeb0>7C*rO@X8MRc$%M&xHR6w-qNNhE#-~ToYps9&TuKOBLDdWqTj9!^f##}`8NAW zZw$wZ)(Y9~?5iPcawqY*CzT;5(pN-(4CTpC(G)KEBb+*|)CwI6C|x0*OS{gR83eVb zgKNs7+i?sY{kc;btT#aHUskz&wot=HbZhDLujk=LNkT|g9onq7hX6tO zyNDTi;Zd*clp%iYAAWq+tIj;z24&B^KEuri>iCz&o7a8SXI&eaYqITp#MVkR#g6x9 zxLp)<%>9sKb%IXj>yFF5RS!^8R=y&KW3e^kvuc!wMySUtnOnPF>Z&x4xiOHOxDye` z%9_ml`UrA0&v4B{?f4wo7g=_A4PV@*!gXzI|qiX@yF||_v6-U?7bwa__j5T!{TxR2|C#X5$R1)2~GPnZ`M(3{t~qT< z8#N5oNA1TeqK3Fzh3O=npJ^AfXp9>y6<)KPW!Er|)L8iQqZN=(IlzM+7JN`l?4t@v zs)lFe^>k&Tm-(Idb|4$4Cb{}5QIkVQ2#aY}3i>O@QV!^o$gcM_L=7p3*WFK!VG}Ri z=VG7ltvvA_KSw&${DM>(MNdBbX>cIU9bFEbGR2I+*iJ1yl-e>Xh`o z)sdPVt(I2Ar~I8WW?aSk3tR<`<%og=Sj-WJ-Dfy-vS#AsMCkZ8QXe{<{XW4|arXOG zkHM*S;+;Ls>AO~{%dN?B+J7`FZEfZR`c@kh?z=9b8{7D_bj6Y2I++HY{i zStrWH87H1S>}{#~c5`elJ3rsHix8z;tQk02-w|cZz9Tg2TJ^p?UQBFeg(6Ml)arbo^DG{z72h}VK*;2ZsHM83O$@PUFLtM%;NPn0jYcZH_~ zw#{71-pk)ozF181ruzVCrKRDC39im0S4hgDAU;+2bLT^rxR;KQ^uG=g{o_{uIqL+H z4o2+2(H{MtV*F2T@;_eV-L1bwaHW7wOYomWD7h3U;|;oT%m{q2 zKg8hls%_!4tr2qF2u>kZ8PDweLFWvmB5!m_^fLSbBtHFQMfskv1 zD?_B5beJD%`D-SB#~%MnnM7{y1Im@*?rY_pP7@|k*=tKM=Lw5P<94zwBPII`XXHEX z0HP*aJ{d%v!xBBtO5xU%qRb1A7b>=WkIJ3UF<*|7)s#xT6~n@M&oVsL;~qf2c^>W# z-l(#ko_(rMZk`Snh{SPr$$137Lv&wgIHyKzu#KH>hhSbFw549}#PL z<&Lg&R@k4a@na_)HEg7lLs?h0^HYu%ZU&wJv~D4s4|YuFM0ix$KcG$EXq8`cX`s+V z7F0#+zv#GaS?Yuy;AGyF%K2$`o=ne>cB*_h(LMP!wLNASV9k{G7>|6#{cfSh9U)F3}usE zh{GG&Llvd=XiTAqGM%l6d7j8#KA+p}FXmj_xMZ)>r4LNdax@YdrRQ;9icblguFie~ zjz6mJ655;%5r3rPZP?qK;@>pu+n>}1r-4>uKQp1n_b16rtx%_HLdRA+=I--{3pwfO zO*^~Cr3;A+2RnvoUG0B7wA;fl9iw4RZR}3ak_a?=LQXSq7PI&ZI z?Qb*KuNJUB7TcTs%5SsX?gs-u1Yon_XfsU;GONTRDaK;w%j`lMNq%g4RwKn3A_p6_ zd;O0l@zAG}G}d_7PW7Y_3EAsKd3hB_nIRjZS(VlmyOF!P_U%EH{%-D*9t-hFT>Xbl zGhy1cwzl;v;~tTGt1E*{(nEpw;#D@a4t@%T*(BDd_tIe}7NsTa_wN^!G}hX#OJH}qpK2;EX-Y=LEzDu=jk8PsK2Y^SYkNrhgHZ%-|91(g~DXow?Z`W;SbNzk`l} zPv}M5E5$KWL$UYu&4GgWDh$#-F|ciAC}*NI_)?Icl9Cc+@mu`QDE1b57O0*97+og6 z-FoPoN;ujCgZa#6#|6$)|Hv~TY$Thx!E0ct6w7mEl@xafS=BPEa?syiqevY{*+c`G zxYLEr4FI>bZ>1jP-ZRbUy|l$vds&xS=xZi;DQ^{uj;R z<+9e^v#`LEUtp*j%4dJMOyeo47fjQs<~KS|?=k%hiAg3_7|K;eq?}1DsFQ!!{t!~= zcXgMXX5*yB2)^ZYmd<*zeSKZ;EaZ5qi&k#IqQ|?77*Rn`LOUM8mAWQkHV%CmP0JVdNU&lQ)w4qJavz* z^}ndT#NI#hO?^&vL4t9%!iDbW+bYi2O8Z%=-vPW%oTd&)@eD$-9fLE@3zSm*j&1ow zN50@%=yx^{pia#@ph-{4#KjA-`0G7HS1)767|EVu#vy`}&A2rNdkI@iU(yPq-xo6SOVBR;|Cikliw4?mMjHP->KGC#SqHX<;OyI~_p?4#+bRDGaz zPrlsgmYP(pv~46cOv#{S3fq8# z#@CR;*y>I&-dDAUC=ZmIbt&o<9fU^gRlvtp9MU?ZPiXI3+NR;@oJ@G4|BnhFPoEGg zw}CQ9kt-tSpdbte=hl>NN@Las=9p>mw#V}#PIHeE1?)!^xixue zy1ufd(40)4RN#`;=nx04mtwXP<4!RX9qzjj>r^+L(o+IdDo|}OaZZFpKQGB;em_lr za7$eI?#WSCP~w-2v~^~jqV_@z?u5;A_=#3Ih5y0~DoDqVx*+vAn!s;?F?L5{QlP!* zMv2nV_n?CySie9RJ&|z2$Q4|`7@=H=kK)VTqT5O_XmOh&Bcj9g5=HZ?i#!a?MH_ik zyq;aJ8&`E|)*?g$!_ccFZYwC>ze1 z(00Db+*0~!?V$rDfq{6vIZBB}VJLEY9|rU!fZFWt^|wm)BlWe?aq)&?J6y8(wC zyGzP?Z6C8rv8)^3#Jza0$FK+G)N0*vn95R^>!$8Yy-!}~u_vjqVelE74y9r6UzQ4U zZVV-h7R%cdPYFGUXxD-f@6SHTQ-p25&{^QX-NkM;JU>HLO1NO^V6@PRv%h3^uH!34(&dD zbOF6SQ4usv{YV*|wrHIie+9Q=ti_NuqGhUG!fR5WW$rTPU}wGw^>ie}KkhoL+kH_r zCke|aiP!#6uBNTN`m!r<_rrB@U8al-7mh2k&uqPrAt_uoPwoqN-PRANGR%XDt@?v=11@LIh)u8$I^Z zwJp+yZ}|0VHsRcGr!k2XzEK11Uz+;fh;j&U+@D3!pUzB83ime20(2li-Sd6i&AXyV zX)fSm@J4d(n~~7`NmbwY=Au&U$`T)W8vhz?FDGkWPFGJGDekFmjZEv~WKCJGs@WRy z{r$iKam21!Ut*Eb{zy|7DCH(yEUs=b;d@4NOXHUqXG`tYcp)I?RY6<*`JvNMn~onA zQ$!+m6%5qjp|>MHF*JE4ybiT8(x$5C`mU_=A$NK@XjUnN9Dx>(!(QuC*Lj|G>rv10 zE7_VRvw=`X&f~5IXZ46Yd|9~R3QL{mcN%{hl7oiRs>#4yMXCNL^S5z$S(iZC(iMh& z?IO2>G{qNYXX>`9${NL<4xcp~Ehb7eG}QCX65m>=E?EAoV;qWa-y>;HgnrVOuI=?@ zh)>JTrSbhPx%6y#z-&lRTA;z%yZnRRx9llMyN0Ay{PCjkQZT=D^q9nXTo|v;Cu!k4 z11YJS#1dNIvR&4j%{c_~(79dEwb%NTd%+U_Pa=aDHPX7((WqvR~2}Ec=_LnY3-1DKIfIK@*hb*Z6)|hU%kteGWl(Xr1wakdQf)tpn-aMn zS(Eq8H9gKMCdN>rd9D9j!){w9X3IsDdf=not22P*6GZ5r_@VD5c%vY07%sNT6}OyG zks|IxFjl1tHIUe*X3^N0IXaw=3_8fvVA1-(mRz{J z^+Cgj!9x(OIiCSwtd)5gP2etlU6EeHvw%l8%T~8AHmY97&{j@{udCJoVOEyui(B*i zbS|%mIMcqX%2rs_e!Rbvn@Mb;9kcy$TcGEk6-GU3LXFx%SQd`iC&#ohM_+hB-R>DN zA!N+R&#kQy+2m}DjMZ~>O6B=5cpu{Fr0)6T@MCpTZ#rYqPC?in)%JSL*i6oZ!-Bbq zTO0M8I^sUJfHag%;PC2S{aqJ%%+dyQoT;^vL`B%!e z^33on`P5`7=Q*Bf$4{*7Z}dBq2EObvt>KDNf2uvAixC*B^(6<^e%#ueUDdHtUmHD1 zeil=MU!D+(%$KkMmF8z$+W7#b<5Ac3VkGP_K;EBNLxMv?jvH!;S);pk)js()=KJfe zzC=cSyjduaOg)TRG!0n`lt5>K=F^ij39+*jRxy1CofeT3fJbAgDK`D60H&o)^|B9* zv3UdU%H}y8`c}^~bBWir(8&4&3a$L6k)_{f%`-K9EZ8PS3Ap0msP@+`8u=;aqeKPZ zvM^I0AKGA@r+KSeF(zZQ{m1R;wFfTBOS9;8%H?T~p6^=@82H;8BLI3*U8Qjg8QCl( z)1O2XKt-x`_3fHrkq+H}@;xdsTh?7Y(}+!aRj1DE40x`!KOfX0SXJ#5zpG4Y>MTLBQ80f5UIDSf&>6CSoz?)lv2oLBW1 z;v=~T;Cm0Pd*c7VJ>_};0u)9ZlJf^|vOH&DbQw(|{TIoRWC{bE?~4CF)qe|Fk^SSr z+4~_JzerGGCZPl#LJi-t_BsmIANf)~2mrpw0}yQZ5@@YL^e()}EfEATbO%#Y3slwD z*UNY0_$-x||6^EgS=N&%I%d_AbZf=cX(R61+!R7S$mnyglY6K=YFYORbd|r}C>7V6 zlP;_kFY`N{I~~*Bn3r7VTIOW&IJY4+xTw~!V;U;J#a&ijuEAw9GeBnGu7LH*OnN$z z^tH<2ObbZ;4GTYc3_`b2LRQvEM|9Nn^lS>;RxOp{?!$#EtS10|-+I^t`i5@WOqK$r z{X{;T`UW+!1dMY~isOMqooeLHi&%|ZwV^k8TcK%$ecZIV{aAJ2j@!|e1Ex`j;?l7m0b6H@;)TWQh2U%jm7WSiAcXqKMKu!>#3Lgy={J`hNR#p;Y& z?U~Jtdd-a903D@^=R=gnHmBG4DGednw!RON6cYH;k6(a`PbnZt*w`+FN>cJ1Qt&vY ztLcJUNkJ7Zxd?m9AN;=g&?C&q?{S1vafFdLvS^>Qhl-0mWq16n>U)T81rNK<0oA7h zh+QT!A4CjM9rQTuP<_~@K1BImEACy7T>-MNij4~p{xRKBBMV($twZGRJ?^Tg_C0_D zd-U#oQdGkZ+<3w=b^1VDxq2&<2^_su`g8fUIUphk8dR|HHPksYM&g~lEJSBAI;I+< zbQgJW3yY(^QOf<3ap{7eQK!LS{e4U-vP}{E}+@_e?8Iw_! z-y^$m%mOLrsnk?)DyD|(LsWI7oD_dLvqH5;R5V&;jy8AICY%QdBEZxhgJQ7xU-ajCP<~$A4abzCE(z#;WwxRNZfzV z#RLJGY%{aX|82kvFao&Adb0;Ifb5BX;c{D`+DL_toB9vS*pG9TGoJ7pkn>W^UvtSP z0!>JpJeq$SB$?uXQ`g0|(uV8LZjeCJ&|{^xKa{+L7{4fMxi^?=)bZ9rD>hh;r-U( z#B((hcC4iRqX}(569-YY$e+ol&dWA>oH9rLXaWaNoTl=CedLct{*eRdlw$;4(H~8a zF9VuFrSg833H*vB2?9pi#bwGy;NKPe%@z2s4%cu1LQ9-~T<}L|8UQDLlDS&`pU_R` zUmTqO?&iE!j4DXucQppkD(MIaa_N7osL*z9`YRfSAnWo4YW_T3@pv&kh<0MPVl1mT z1Yi>bn7vv2#0^c=5=StWEXAi9u=y#3<-C5}#&5f2JhQSQnv(J*(if#Y zi>pi(>>>qn%>cXR2-vmbxi?j=n3r}1?Dy|ij)=IGXDh`blO`%01~Ozv7PoW^^%G3A ziW3`0hc3SgoHNBWH9Wjfks&g)jhg0nt>DGy?93;*ONP%m7kH`oQ*}T_^_Gv(`zKW$ z@!!5ZjQ{w^!aPGgo}DxBQo8`H#gs0U`Zcu+69@FQ>2%(`9tpJoS}~o*JM9K-3d!9n zGIU$LzXi)*EoeH^1R?{^p1rZi9eHcCp<8^w9WC?vL6ky*sOgOk2z7-KLzs5M6g)cx zi!N^qyRm`nU8e?;C+5{YrsrRGV|g1yYe-(-p0ELKrA4ekya@J5z$`w&qyXU6v4H2G~~i+2;P4>k`MxqVE-n`{GX9{1gnX7t<{a(lBeg(``r#; zQhZl~Z`lz1+i>lLq#vau%@~646XVZ){OrgCaGSKJ^IZ)8GxFLeC?MnGDm=8zuMMAf zd>M$>Dnw`oKX3RGkKc|=+xDbw4vN&I2*KRdhj>oxxO z5T0b_u_GROj5k31m3w8Z-=u+hhj}+wX922RP4t)am@X1-8&XJhLga`}9M7?T`e$r9E%GOz}&Qq0yeIGFO);SN)}biLX3P(ri-NUZSwb6ApIzq&=bl-Vzl%nn=&v1Px*!0oph}9i1FUsFz5jH;DHUydKP(99w*bp8L+rF+s)5JFq zM^g1Q{#N9HyP+kUidZdbzt7NPqVH8^U{%? z>>nC7u%DfZ!XA*0Fe!YQ5#0rE6jN@MK8Q43(#7rmV4UP=h(zcBF*5Wb9JCVFG%<~< zMM3MZ`*lg%;w=nLGvCWD_EpTa%U0P9(bl=PDvxF;xcKY`f)9PCC*8_QV28!i`%|jE z4F^d_N_DtXCwEjG`Q+NzlU*-V;z3#R>gqeaRQQRZ=)nM&gVK=>w%e&Tov}4rTr8zSx>cr5lS|7WNoEM1dd^>x65nN!unhws2*Ap`N8l1Czzi4{wy1 zTJ_KhqOp|_WINPea|*e|tS;8=m50!E5G0*eCr({KAY9z|-QB3FOW!iVZIMWXY!fxv zKThjGqYcKoiB*=bxKY)ESGPp^S&jB3qmrRNl~P_RZS`{EUFO8|6Zr@21uq!2>TTYb z4>2MoYdTG~uDEwq5G+V^@vLI~WJh=MbqMcn;BnVy&S0lfoGCco##+p|jDEL{vc8ml z{OD<7op)oHZsUlxex>7^aKAaC#4-n+Ql;=M^+@Aq%pJz&aU0hbtkI(I$0?p%Jh8d8 zrQ-}|%TA(O16&)2;8Y7#iN4LLDE%^l-@IpC>D8aVUvrXciq|i`Oa;#8&v!}z8fncF zXn7VwTVAhMU!X5ot#C&lUa0oQy@#`*q2+9spX=*nox^9c$%{cAJ}#7_~@d}l{JqP8(Itmk665-+*SB`w1$ zgSrDGb+M)Kg+y>84Tsv>1jIVz&_YvTD9OuQR)Qi5Ulwa%1L;>-o@vi4IV1e6qY2zx z^FMuheYO&}w?}q&ZFg_?ZnttWLeF+&aYV*%LR33y3Bk}d!G z)fHfW#gre*9ICeFE-uX&7ZrpuG6)`<)VYkKt6V=jgT0g~k@#EXtiv3|o_;GtlQ)>H z3aF{}GM%`kX{nIuu9gQfrwD=?9azOmm;S6rI2YZ4r znFm!)rsp6ePy9v`-F2EYPYjt&QcraZK2R0hkF1cUJ6ozuon*|IdGSu?0?$xo+r#xz zo{*0-5$V-taCK?p6#Hy8Fs!%f`ux&oXvHR^&cN0y&)8|1#$KzxlL+PV9##?D*Gc^I zfV+3^MTgKGS`hFeYE+&=zKVd=A#FL@CfDr$NhZjZzRTl+Yw37$P|MGv{4%k2nQLt@ zC))Vo5A!8%M{B6xLF4Iiw(0SPROe@mP&&Cp5ccEumj#10#Nnj@n_y~EjZ)~g=)l3& zjDPcuLSu)XME(jTqHuR;y3LCCsCU7uK?K(lk~#X=b9;=opKTUuaQBd2=Z;RH33sf{^+@{GxQ2t)h$?V;g z70`GNZ}JLTZ`<0-GvYcwgRKn|<{H!|-woOHsZ=4{b5RQ`3mvncPN?~~9K3nu=9~8> z7jkv$I4-{g%!?L<4a$K$(SPj2bZF+1e}ltLhHM@DDjBK)P@M4wUk-ZU ze|cqg6o4!AKka5x7Jd|arGMS^*10f(SexM`QiWb=jYhtm4fNDl!V2neajR--?wBM>jhGa|6*NDk7oYiGBvPL zzTP2hWBl974ghw%+te)QFFQT3afz*Ghm9>5PB_V80RMga%Xx`A}%KVBvv zAy&XAcqsMmpTGRk@)W>XTvL`O{;TaDzzt3!4gK(!dmDuYoCkfVw8+0cFfeTt@QI!I zCu&)j|9XFgY%lnFy(tI(xQ}=NPXIsDI`HnZ-%j@VgDk}X&v-O3>gwNZ=WZ({701P& zNA1t$Fs%XZXvNG+*I(Y*uQp0xCGlzUw|<(uU(3-~1r`i^Pw3)bZSjCp-Af?b^_R2U z=?yHnQ1A}@-)#iIO8#FFU8V_1C1kRfR)Dim$HUAl{ffx`b3~&$936i=P~gD`Tx20R zp-Kfw4We!sKRaY)d><;`&7O|Wrqpf9rKA!Q*)t3`^jI5&Cz=)kS>Qw2?au2G6DM`g z3PwlkR8bC__>dC900!))wNK@iS{TJY2>bn=8-{lKPPgj9QGkL?Uk84w^!9dL*8(bf z=^-9xKW@5@pamy3z-M{JUen>D^Aw zZ`E1nmzB+fH5aTjg+Hr`j2zQXHJK#bw%=Qx=dO|(=y4_&TtLt| zw#H?>4r+}>d3jlR9lwsID2j}R>l9!%M!IX=x6Bw}hyGp8wZiHMr_V$_`|p|#>vuO7 zmzE}FU}u>zF)`UwV3Su)FfXPVyU`6Rd{K*oCju<)C)W8!T?&!Sz2iyq|A^=O2yEU2 z?x-`Psw-z5``DZlua_+8}e?B0_wIh#vHXjEu}-Vvq$hOd08=y3=xz$lJ-kqv>>tp&obO z*d#_Luy|L~rW4?X1!e?SBF>J;qlp#F?oh0EVUY#A6AHO+x6xD19g%)1suIUc8bGG0 zoXFNz>SkR>N!`inno)z&Ms}0@G6A1&Ys_0JLT-EwCji2L=OUo>~6^GUI@ z$q{eU;ndYt*z1_s*y>tTsjVh>ukKC9>xX?+u|)b|2zsq}ns;|V5 z59b5L0pHz=M+zUemdz~I+8!>gl^}=vavotu0v%X+$|rwVvp%lkc(PEN2z#n3fq_+d z4oElo?|&?-0y6%5c8;tY=p%$yZMZmr+iHOhGK-B7fnFOueja;?w`R&8TY%fevRBH@ z%L($>j}=CIfK}36Dswk&a!gM49~B<8$4Shz*9Q1l%|*XUtnKV|ymNitvTC5meEo1t zGh~mqejHJkWm(a16gjq0V5=!EI^j7w_OMVDb#Vi&Yg*Nps}U6|`GDF@e;RrQqJEQp{#C@*?u=+M={I3WscSlMF{jdiSEa5t>934mo}T5PP4 zMq>Q}BjpW^YZfNEjB7TlJ@>9O3AbiGibKfi8E2odvY&cbpN}6h` zKI4=sLSQv8tT0@3$-&*isN)?Hoh2H_k=@;Cy?7B{_;K3tE#?jpIS=PB$&iSc^s5i8 zwL<68JSdVWd~2(J2t)H<+B8xQ)apTy)dP#Z2Ss(u64xye?mm?VCe~glXauz6lzFm} zX@`nFv+&a6Xja*GVzr+d4={$9R+qlRsPKn<61?TydQqL?zRT(+PvxJ;YspvoW4?AY zird9K8gIWsw~?hOS1m#(S|%_P_U?0}(BP#QRf&^kpSFbuIx|q~9#`o_0n=cARh*rH zS~4yLcPhrFVhE%@YodtX-1I8^Q0um}(l(YbQgDKq%W3oX6JaD2{ph|qIC2RA==kJz zv?{tTbi{_O^4@C}hpG>;$ly4mMFe*e7)3gJ;T`9_DLBuBgtC!WtzL;?-Drx}szUVP zYn6*rlz4B<-;C&*oFqoZFbEy0rOFD*Dbijt^_6IY?^N2k4_8<3=iAqJdYkP&lz?uR zdF`>XT?2XVbR1!`!2-X)e_{$->Q7r7dq3!%QheL4YbqV^vDTFO&6b>cxdf?A+L<~19SRPYI}9r zjpRnBrUNGo`LXx*3z;Xa1|3r$%i@j`Gptino-)F=8!4kZ4(aqZQrGo3Ie9P{dV;k$ z?|Jm{>d9{KP18j};Im0$3#kBK?&`{1u-kpRZ;w7v|F* zB)pDeU`K1wQO{810&P}i2>ntp4rS^;z^HVQ=ql*6Eyof>-oA_!h!G5sEf(+CBvB6K zVN2ulrptcW1NFZ?|rE+AYM8&ADZU4S2JV zvUvf30ah1?g{n@vZ#WFJ68b0ed{-oUr`PBZf5qKovBCIt=h(x>U50Fo?C7SXeTYqu{s1~;|%b*}M{=M!u(lb?L`84iTB9K@MEfy*Ry`Y*^4)6PKi1-zMIuj>kPNo(J{QI@KqO!fS>*n8A_ z@0)Io6B~IWYlL0hs#fA+Z@dNKbeXN!3X3geo-h!MK!Tu~Mqu(g>^Mn5hBlun(N{69 z7E=h6)JXlj0SbX?QGgVeySe0KSuo8AGo|#zm|Cn{9D}m-=q^_;rT7aUGf(J6^yV%A zmMyZjz44@A|$G6F5f!&h<}q z8qBmc-v6-@g=f^{-w;NB)?Pv zBGqB1V?#~5CT)$I4@%yPz1`hZdduE6DYy1)@{t0RU|`;i^aT@PWS6Pm3Zba=KB2$H zVk~zyX44S2J7;wCV@1nwsV7;pM@8z^@tJQ|uK(SPu2`Y|Xrec#7pFYSVPJZ}YEYY%`F zJlh6y`X-$Ld`=E~NotUpj9_wZvKdS%|LVsmCRLBXd}-3T?6fH$@F5FYd#tX2uQw}3 zksFiON;-#@5PPKRk%8d&!dm@K$7I@fA8Z+gn=Esg(mS;Tjrr4>N)3AUD?@4(mWr5G z6uM`UIic%;ZpSL1*vvg!;p11j?j$LWEFvWY(2;lZO!BngT}!U)ZuW5iKLJ3pmrlj{ z{2|!POT`*E8KPskf@5yp6owr|8eWek7EZgIWXky6Rze2T7?xSOWU3q2?{V8_m{zi1 zty*zvU^v)95j(gFCUFGO87hl2hT>W@4R`GrRZoy}8^o z@?a(Qtohx7lJGA-Ru|_RQD;b>f=F8TBv``shG0xIkM?3WPvv7Qmx{>t;Nvlt3oK>N zqb^bQXYMa09T+M^e#?X~)Pq!0z-=v@D~4wG0@!!B=6xScp4WM_H6bE?XwdJ*%7vLj zp0q2OUCYy4(7W_y?=$(7>1VDa6=AX~Gz;c$+T_y7$exinnh(2^`mlI0U)+gX)l(dH zN9!x7>g<1!RR~mO*d9@N-R`VI^J0{1o!vcgt~Dr&E?(96@;>oaBV}PIHu>A)24 zb|^;^#{8HbaW{_Q&r>zoF;=da&V&W4%HO_kRNCwVVcG3UN>ihcSJ|7McY&KyUO^Z6 zcT?WKo|*MVZ`X4b8cM07n1|Vkr||9ED{x}sH9lYvzgx1PiRmt`9SxfWeAEpR#3!JL zoS7P*4_%?U@ncBkz-dw2upl-^xWt>O7jRlMXabNW+hpN{)8FNNA{awCsITU=BTKud{nX+gYM(Pz@XY-cRfhjpl zjnvDQnyaMaKv625Oi;b^EkP{7n1`sNyav6}o7I@_v7IB~Z_S|s6DrZn3xuaL!E(zq z>NGL!pUpN$f+?EvUCs_%8V&%s%SHCu&7#`HzQmP!Z z4y2g=%rfL_yR)kVcQ;3x7p2H%2!r2~FF{Gm^>=xXZy#hSQ*xqX8I>Q8JZfgreqguO z$DCl4U$2-D^+}=f9?6e?0#=JljW#*_x=HavnHjt`*?C_DUH$5V9-faR1Q+<(<4@)} zYK-TS8t1-f8>g{;g?X4TN zBj+5)j-H2*87@fU{Rke;LOfwZE1AeB-yM9!s2nj^8!6%0SEB1zuDq@DhKbOij=Pa1Vao^e`iRhVqDsThq@3^SM&b|^(wOhK<&Lj_m9kDQGiY(44DMRaDKP!43 zZJ{dkVE5I<4Z+XK&QrNcH^H0xoko!Z)SvV3UC2d<$kVLxdKPkDE#ny73{}^js+fz3 z`LbjyILilOnUColq8nxuJrlU@`j#>XF{6I#_^y-0P%wL?o%TXaL=pHHks`mGjI3=T z89Z!~Fuy6h;P^8=i)(DCDtyIzukkRdveVdqkQnUaI-# z6q*a87_p@Rhz_^J18-i^toGi_`;mI(S>qNX^z_j*KO!TPViD?;j&_t4v>yyjQDk6_ z38T4SrCP{%jGtCj{YJv;XF7ZPF%E|L?zT@9EKkt0#LRaQcBx`Zwvag)*8EXz9xqWr zXLt8msyvO#=@?xLeTM$;*FDQmJz{i$+aV<(J^7CGi}xeo_681V2ECm(%AA^l@5<7d z@*=Xwoy}ndMrH4EAA-In-o_pF?UgVpxr%8s#Dz~e#h)B%PX_VDds-Cbi+guzy4=@s z?Ars)=#Oeo%|*Jz6wy6uW9CX}a*A#j=#_iZ%f;MxYr&K^*CVfc2o_$a-cL!PJa<`x zC3DOb%sG;6)*OvtymF2fxv~az5`<{v@lX5T4okF{4(zZ}qP^6vB`?md>m6PjRz8?T5 zo{cbprcjM?`p4ST9wrg0Wv}G>`0KGg#KT{b$)5E4gRAi?+cY8HnD}#BuJsehddURI z?Ow^5w{_$13yEHS=krf`f{wan7tMt>YUoU*SKLbkU>0;YR=z6LEBM{Ax4g(j66u7Y zyAAxAjA8%~(_2h0gyf0JT01gWyIZPi*`k)+JXR_nnepz-grxK~{s@rdq5NJ%RpVfM zO!NncjRFDA0`gNQ$zM&~0G9gyyZs*($ejvDS=nIVyN$03wfTvEW7`3i1g;yS0!bgH zffsP6!W78460n~Uvnx#iFm7AmYB(Er_7seK5nZ|{#aTRi78 ztbPPCb0PvX>Hiuk1jHXc+uk%F{G%dj9l%Q5E+?)2HMCU$$Qx3r>*xF{5+ZV*H)Qc$ zi}=sbYbwBkpCf~Heulq(t!DTEuoC)iap zc^9#j)f5mdD5muK@oU zf&akpkMED22L93j{~sqEKXwB6dz|ol64}w44l_#fUB!3zw2#&dk1p?Aqfcy0l`}Ww{GzPZ}2&~+c`aTHhbP3)bWq}kXA3;p>=+^3nF<=-pWIg*YA98iGpi+~WnpunHGfuSJ6 zw~~)7-OOxs^;pqPW1rg4N-vWS^`3ofQ2Wc{YUEqp-ee9Urv4`?^)ZLCR zj-08;h3w0&IG6dPd#XGQ7_#1!T|CfXEfU2t=c}|3Ul0)@f7!TL-Z~5;}*?z7f{fJK^E&=^FarmoLgdC zZAX;QD7!kH;428Isk=} zyxdARwI(OWzm~oLDm#6+vH!8MB)}Q-6xp9>{6XuPX#$=)x=z3DcdgHX z_c&Wq!)4<5#qoRV)hEt>!7U>NM!dI1=C-u<_(xt$o|_x@XHj*W9)_i>#P>G0YKV~t zrCRQcvYy>LjcF?NVBz;f!kV@AqlnWqaE>b4TWf}^hz?x0Mec9f+P2TZp>cK#L$*4thi+kfN3tPp&$fZh8E$bJHLJeOq zS3PaYv2DJT$>rP_6XId31p#t@pBz&hakzu^jOs4f!Fti4RbeMX<$4-4V?09fYDAdq z^0rBwAz1wZ#}WFx`2jY>QmK0%9;YLm%sMCEW4HVJ>6s48VrY(!a)Z?3*P2cP*O8G= z8xStiq2;Q8<#yBH-JcGZJNrtGTos0&VCV|s$?Ipxl~`!G-Nhh-cR#Lfv>yG4#Nw!% zz*2RFYMh6Y2pW?KrP=j~EycO?zigRCIKM|kaC&buC~N6 zV`TXj+X`2zK;BawHK$V2jCR&yx_&7#*;23h&8mcwL`nHIV4?Q~)`yb9?;35ah_$BV zZmd0=BU;-eNmtf$b8$T`w?x%-)UjQzSjqGV7KsPs}c`s{OzP@zwy3PlO>!= zf0h=-cxO;zCNVxG^sggnQ@u?_$zNI9c{-#jIQNL?>dB{N4%`OuH8}WrpV@n%OdSoL zwq-q;TtcN)DuZUs6*m7WJ%I|q2+J};|%iWEwK?kUYp=k4v}4M zOqU@TL(~qvWm|&8uap82!pl--v078frq@yxG|`QiVffF4+nGZ7Ej z@kNfDj}z+jjZ>ycmZMdTtS4PI3n{3if#z3F-xM%6u0kTYY)nK2(-gKY}Uw zlY(#GkRX(5*!C-IiCR2Y24Dl;sxZ2bb2JrW>*C%!tGtd(xs0=^S;2{Y`sJxr@24{12_N%No8@7K4L^@;2Naue zDBN)F%m$G-q;IaEw@`>Q-KaZYCC<70Hx2RVz6Tnz_&Nd6f_U#EiaP;_6~+L&ZVDox zef%yh6-{aC72|8~`P9>C=?mrN*U#U}OWGlcg(SW`dbt^R*>IW3 znW%Cgtb(q~!$o~kXrYr@%y~*a<4Q)4p5`8Lz$qPoXBtu2wnWK9L)OfX`||Lr$mX>r z#%scxbmgTSgPm=gIe?BD$*$lzVzqQRVA#o7{)dM|<)JgCf?Vn?nK$tG$-(7wDotLm z(qlH+*1GL^ygj#e{k2kZJD%Z5Xn21%rAC#!=DG=U&4csu@>wT z8uWyDcOuuoQcYMW(j`vRR#!@O>M9YA$NZod1t&6R%H`i3;;g#+3~%xJ6I z#BpOVkji>81FoTEkk%ImmpK|$0*I19g*o}d=Op~el0k>&5@C~$^!}cEmVJ@PsTiY5 z^qlt}CZRa~y-Ut1Re{LEn-ncpUESYY!YHi3&4a$tF2yfDsfb$oP9+d^F<^QVOm*ft ziXI}lLHI5#ND&MIJ}uKr3Q|!ImM(43PCrHb0lOLizW`L0$`ROaKj)PROr4f}_5o@l z=DVQa>+moh+TX{I`&EqnqY&m#l7k_mr>%%0-{Y?F_&WwWCFw!B`l7U3OfnCVcT1u) zbu(*3oIOagzPcMA(DeEdjYGDOYd@1CYF%8?ooVn?S&zHx-KQec@{x~9yaVsz%E^y# zgab~p9FJr>e4c&ihxh)D)ywPq`c9SaO{>)Rkk7XVow;X@^v-%D;6uR6@0tBU2Wq_& z;<|dRYE5z>PwF>W97!&o&3ZLv`PfsFDBa~gk=-_Lvst4+mMXpSMqW?!Cz9{12*CJ= zjeuPu;JIkDZSw1ME7t^7i>(_><^{Y5nsrND$wTj7v?{iaW*gJI~sJ0glXNVJCt?Cb$zp*Xp9=_`wuB_EN; z$aet@b%&$U+ej2df({^zWG-9)ZQS9^BmwAsHDFW@hBlua78w+33f`|Cp=XpQuG?}E z@Qdh3GV$)%Yizr{P72dC<>si_nu|p>mFMGgn~2j_UmysQq)#G5dJ<+^0Sr`OKrl(N zR#%?LP$lvi#rj(S*qr5eh^Ae2H|4PFsJCrq?blEbD@D&Cv(acOCz>PJTmh)Xw$_Q8 zD9YN&VhlVi^|D68;1$JbYzE>FrS8Hro+B5L9nWG&3Y0>agVoI{sF`?X;}<8VhsEH0 zq1IZLx}uw$wy%3)3c-4L9sIZw_45JR@aM_xP#e7^1{DVEeuJ9HR0%i9N?hLAoR{5uaoTD& zk_3CdOoj^}6>pY|<3yS~<0s*`Y_BJc?n&peu+4saTD80FMQ-9IlSLNf$k)69m_u>G zi6;&TzuNeWa=a*?_k8t0htJ-eFWN`8ii+EW<}E+`fKwz^ zdJey|6|pLH*W;Oab_4_Zll1ytfobP0RcrxGs?&bZG+STreZ{Rncuiz2Ak_xX3bmF7MMOL*URh*Wc8N zU%qM{!SxNVplunQ=4YwKbKRmuf=+VxYhd=|HE{2NF-m^bM`jKPa)n7PcHHxHSc0pb zQstOe66+Tn0T^NmVR&Q(oZtfR(`eUolZc=4>dhhh7@5sLaEN;fMVQNoS&Z5cyH`+0 z`sWK-XOkx6%>qQ`zZmRt%rm7y*b}l^#rL}*U4Ey13U(v5ju7Do5d67krB<|ur?fCI z4i(V^1NL+0H_o83OhfJNrhsHSay9CZTrvB6tR1;ZaR8#VtK)(`7{g*pabIwwWg3Iov>yLRO45Ze$e8St~ zKf7pQ?o!(%0*3L1sK~xf0zBbK-}-l3$uT^7vt)hj&H3Uv8`XOx!8vOSh-3S$@Ij*B zIIC`ciSwVluQLR}l`-D?(=d`}r`>rRQQL>}RbvJJ`-H-JjyFyD!x6) zS#rWHm4e++KbOCbYz~1pJJQ34OiM?)?wyw%UVlk-ByiV}uSB4`sM9^L!%aD<8NWH# z3-)Jme|Aqx-@7+QXe;=I6>9GyqKD>tHwTt1YknrtZ8X;ziZ1Ijoclp3FfV^GfoOaJ zHV77wUQi4h9TCJRj<2nvdT=naQ^(sVM(gC&Gv^mOsYWQEs-W?=>!0t*bfwXkoh7kq z(iVBnYPY9%io)TUli(+PUeup|lA|N6mdVsA{OjONiF62+#CF%4Uf!i6-7m}mND_#? z3sH=b-yO?uU{=yH+<>1E@^J5lz!gEZ-wsX@+}ewb14rRa!A61?=zy^znl-Z@s$Wy` z$BOz4G;@M*$jt%Hr9|KNxo<-|0Dv110e;NS12~xnw+tzYa)b684(i#dDzz*=?n5o( zz4dK!TJM3^j}tg2f59_Xj-`uPCdzF~^UyiHU+LVjs6y4;D5=rY^fp83M( z#T#D5oYt-@v|S4!PFWh8Qbc$PE|R4Wp2IOE?GwRyj*R+St;t|0XesYp%t9taJ|2eb zb$qKT-NVC8;NIhNbKPBu5pmEUyZbApHNU_7E{Qu? z1*-tb>~j%D(ypIqV@s;Z=5G6Y-9R}aP&eG)A4u>HZr89J+`J!8M-n{8{C3F>lrk%W z!9)S#M^DaQv64b-&3ta68#z#D4)3!q^$rp}BL3rKlYr||zm*?F19NP>)>d>M(UbXvJ{8m?tiq$UYOo%F;ZuIEup zB|`)Ph;0*}Jk2$jbYx#p2y+rO$I$S#O%h#F6y5mRa!U!0+Cm@N9}*62ofqHt%9FlkCVp$XPa>5J>R3Z8(&$KMK)5e} zL-k&KpQ{6_Uw@rZ<_M!m0-`UqI8GT)lg~|VMtoC{nTd@+*vZbKlj(F%yNWD#Q1DKT zvI>$^rf^aR@T6wQNqW|$b7KQ}m^eMjo^b$?WXdZ!*OR>bXajq#qv+=!ES(Jn2-l)q z_ukQlNRswA*$5qw1Lzy-IkE)){BVyQP~~xXy^oLR_#dl~gGV-8ME>^=)g1(*Iz3zD zU-^&@13<00q6|WbBz+`}FR%&<6F@v?b?#p&4ks<(y#F2L4+cpe{=>?D1oj`V9O>gj zckcgp3D)JGp2i5|bp3*=LoVO$WDDkxqt~@aCIpq6=@&q1buvyi6<* z1HK)l@$xVSWAO&CM9u}M-gza5r%;B;Nb?o1ch1S4mrYpSn~?=xmyVo}eL=D(lEla- zd@1+}8rduZIbt3)%k2D|gCa4i;WE1x_Ufv91Ua5$35eUSXMok41Ut#*RBpeCs`IVg zbd(~wm}j%(_Rc4t##+F)1FK3WXWD%Ctslo{8vwHZlG|}>&^a##Yu4jrX>!Vl;0V=K zU)7_vw@yxA7Ppa72UJ>z>&OBjkNp9AE0tH(*U3#(-~(Ie+kxy%Bzs9YN^Wn2$=9EU zpi??vfTTZI6f3?VXG}wu|G_P1?h8q#?XzF-b!`|OKTHCx!vSE(;#6|aT!?)u$E#^| zZS`Tb1kra^P6j4iI#UGcKh6(i5d7$Y<@v}YNe;%}T176Y5}4$V#dV~(2+Bv75Iv!o zeeu6_7r{z<4d3OL`4@9tupmUH*ph1gr8>#y0Yhybx&1G7Czo^|KtS-TqqxC8bZdqH zt6LUk0`l+m|2xconEC&l%r4pZeYWP0m{2@&F)vsEnI){h_-m4-CU})B+uJKQqGK)$ zVyEPq;IQyQSfA&J6>MpM+9`$EY?yXv49)~uo^w!0^Ov9%ItAJk7S5)Ardn`ApKZMZC66a5LUMwe)rqkq ztcRDl59206upfPrn-nrsPJ7j1$-Q9^PaJ*82}n>RZGGJ&Wn@hMqDZX&l7Hl6chCb2 zuXpJvDKsl~JQ-585xYxTYoCy&Z!m`PN`MPm!m;Bw5!(`!cGCs=4WJrR=PG4@Uucp;bLTG~AYX zBX=^#Jg9V2%9Zg?9hS6ZMn}>S0>pOXXj~YG61axrvAvZ6XANg^*NYJ&T_w`#@&FvV z;uKmB2LW{=!z(|tr>()9KY;hVT4auhMfo#83Qe;Hv4_|@8&mD=UTy#Qvk}Nh z3}7bHU$XuUpgF^ylH^5`?-CqYI1_yE5XQzIPS*bgYZAROr~b(8NcXOn0Ej8YDJ~JQ z+yQX9%$daZo6#L4y`6L(U{>#b%Pb<%zrUX(O2C{_yY-Rlev^!zf#pvIUoeS`TBfyZ z7oFmqU;XV*IHyAv0OP?Nj|g$$dpxW!7zgwEjuvSL>OmI%)bWty0>NgHLl7b@#pd0? zB+>Hcw~iTLMj4D;&ym|sGI7W$)`4_frk1AyDbVr{o8*6%3;mhuzh(Xd+JBVxAK&~x z3Y#um#X;7gs*%3&brTDXG2C2>fk$Z>O7H=Z$vGec4zAj+6X;y@#4P2m9_iI*jCoj&kb55RHQK9Og$F!8zeoXIa&C=qJxbR=#6ah z*SCku=K5w5ZhM%X$8Rq0Zz@spelu39xfm`hv%h5Tt29#SI#gHK>j?b>Ef{vFo3ksM zigua|6)RcjZX?FrfvTm$a_v%#Uh;83{9DM4S@Pt3IPj|3IJamS^OHh;WhEc%#m0wR zEO0*WJLY$~^YkeGV7(A_P_*pAQUW$^=&O?Po*#RosQPU;M~59VwEtXFW=qE$ChahI zkLtila(YNHd~aE0=44*k!7khHP#%<9qH`yZr5xD_E*bx;Pvghu^WI}8=GMDZl}0LD zhdSOyK=hq{a?CvhdpudiZS)z$l%O4&BW%${ipD)X(5M3!+|rW7=fXp|uSL)Z+@-;Q zax)e@DYKG%J`Hizr9UMA)w?mYo&6HeeMck97=7YpCkF5&g1F{Ptd^yK6C(>UK~}@!m(V0fAp# za4A?d$&6!nG7eLR?$ioG;rCxy|H^^oHTGHWNbk z&@IwWp!T;KA{(L+z&jGs+-}@D6^ACA-(8?oUx8po`8}kpa2c}g0+M_nhrOzUo%W)J zWn}x-$?eFik3jpE+aK8DtMELV1|~l_y2=;w;`xEXn+Q`a&)&kd%~Ci+r9~p4G?@X0 zmkLkc*~j?8D!tc#&6UO&h!)sa>J8@uS6=*wC<7vp|yEAWxjf?G467<}}%Pe{_WCaJAK9)rF3(Gc~g^&yunM?pVq z_s@ymq)!MpHU)B#bMv6P^QX-dA|}uo(Xj<(KtY_Y7p?a}F?-kp7TJ|*TI+}RE?v9|O#N0mdCi&G!|>-yut|i2UUN zH`eOou)SxxrlBHhs3PN+Xzf|4-PH~Nsr&4G@QBV?@1Dl8xB<>Zm&V{hSqXt8a`_#MAq|HRgY|{*8UuLt_T9=>y6{y+MjFpHB2bolQxvyT@8oF z8eYX83@+H(^N3zvRi|4c)-V3Ej9vKJ*RCsQK`V@b3)=m6^Ge3KotSMhr1wDzgHd&= z&2~XdC^?u~GI6zrg+A3kC=`2;zxWB3AnM+e-N}QE*(se7n3_4*?PHH;PY7aVYjQz& z8c>Ud*tFQ2kT*u_*=l$l#JS&jViR7R?(<^$GZ$GzfxHEcUr#0+2DPi4OOLPx`|fr% z8w3gFRG|5h%W*(D4IXNv_blRm(ALn5;9OEghU?`Yky5ql#XZ=V-AOP=h$*sa%ol?k zeODvw5Cps`12XB zs=iZTYSw66E!pl=cV|sFsNkDxc3%+Ayj7%+*C(ZN=r^vAmnZLAXQO+1yJx3f8J}Uy zPfD$rzQ9q_K`otw+wDm%UsN{Q8@0D=x@T%XsdAIS*Such6~wRD4hf$&Ml^ymD=?L~ zrdDU8_%g&i)c7NdLY0i`Qn4Zv)7EbYbS<(7asd_X7pKQE#iCcysCds_|5? ziPz7AL9xmR!$?@a2BMZC=BCg@YI%pVSBU}@(}a?b&V+7{z{;w7*$pLqP6bk+El!_PLz zGSf-k*E1jW#AH}upo(Wvx5I_AwM>L{->N>2e73+UNEb21!6%&ff254+yD-GfnkL zd_YadSwA5+GmY}tvhi{;VSY*Ptr2bEC^8)Kc(q~YVWiot?y?qcu*F-$bMcvPc*M(| z4z9aZl>Fa}ukQt8^Ag^)Xlv#1o}P&a6!OU7UB$%dVJ&Lc+Fy7vv-eNTd8pa7@3XMe zXLE!F%BE63iAgJO)07~~9iAp;@XsQFSVTYO*b<;P{zOez2Ptmh|Yx4ffCjN5ohbb0(Y;PNTLQ43LGF_9mk0LLfp-)wB zTKf!U`H247rD}=XThC|0T6`;z$J<20c|;+qnQ!k2aoqz-F$0c%rbZH9fCLipjB%vvZ7#bJEyP{u_no9 zmgzGmARF9wDQay8zl9G9H<53}jc%dU*kPJJ(@k|HF;iUkdI}VoZ7=qPtZh}{rI$Xy z$?6$a0w29So$sRVy!-2&`@4YjlXzDq?^A zbslvA@(`&uKT;@kGM|_Fk%q16?!gu$=cQ(J%+yz>oX%EUmac!6>Dx!EZ(9o@_n8ow zScVH4iZb;mjl5C%hS~u5_SPMpiG$2AavoWhgsY`(K9W zXGCZ8$zmgu`^6qf&}2$#a4QVgI^|deo{Tc>hVVWC83z86DxS(iE_2gw8k$9Ebd?O* z#&5Pm=A)Z}GX$a)fASVI{z!ctF=&e1@DyA4=3+LI7~79^lkCSgfNcw$Lk9?^|ZHf2Jvh$+p^00XL8FE6DrI@fbaXHACD?g?5_x%dUIh z2Dj&`MtQk&xP|STs_o@JBL6J#^Y}~U5$i9YkC^WTAd_0i!*QZuq9uFHDMm(!L@Dcz z9qc(3+;Y>s<MESb$$ocsjDw6tE4^rCn;zN)b=nu6U*$yp^!J2lNP zFamt>BzPl_VQG7hp-P+A!Vpt|TG0UjH(b#nFlb=EgqVd<;G5pn!0R8SbhYYL^etG(=w0;T31O^PD_a zXd{nzbscrH9m?>y4(DmzTNu=yvd2DZYPLcP_FPoRM44OT7w z_pVuLaQdJ|B8uqb9JRFq@F`zFu$-}+Xg22^lCbsim7yJwx-qkiV=Y*zcMaQ0^Wfw3Z??FlY=2o3VPU zC{hK>gsB;**)=1kXiEm~FE*c;IOA*=Mp>wZ6j-=cO}{Fa6-6IG+{a@f!BW5_kKjr@K$<%n!mWC(67gk?WNyJ610bX2BO z>?PD_Z!b{Is>dPI;yXhHW1m<_KCypjC6jd2wU)3oj;C8yFU{Y6r!Y_;+eOKmv`yf_>2& zSd#{`KufKuIOQqx)(U`aI4leKpxPFo*8y&i44n<)-I}zVchzpsYR9BW_GItbRP?EKzsy-M@LVdkta1MiLCFcx_vW1< zuiC2ys3#g?6C~S5EmJ=+X12m$T~l|n zSUWhYf?=Y48gRq1(Q`L8bU?qpdS#7&Q5|C?8iUnj_{34c>dUO9Kw2JfbF$yY|79+OiwJoXTo1?1p z#V%i()f8D0O`6mq;W8VUbH5>ACc5{+ju8&Y3*t9=uW;XkZ%adm#WVvSr}EyW?Y?4j zbQgd*zmV1Z(N*4S289aWg$mF_ZH)6Q%+8y)ELZZd-fc$?YWspkO;mHX@@TyiaI z7_nK*xP9NE#Dw`WB--9#)rpTvjaLP_g-^bm#v}j8P7qZ8k z`oa*oRKi|Wy3h*4@6AL&mJ;A;bud4x9W`jVcDWBe5|S0KfPyW0i_BmDv^`Z|quGl= zVt>?cJGu^URmOxhwe`^K8#0a4dK=;*TeR2lgP{3rkok__n?dLL`FF2`q{W~#+Xv@; zUR+NWHlU?pw%xYlX$hD91SD6=~XAH}HHFYz0`<6%VfH^~-)pf5+ zn3PqHO8r-ohNd3v#S;3SK8Bp>=!-*5j&+f6hwCau8=US!8?h7EcLH9?mm+R)B2qQN zh65t*`EGZ?I*qUt;dBL#ms28V;%194I(uy=`_CJ07G0fKD#TP;{&Ej)Z}5;pG_r>; zH;#|bw|n$vlsDW=)@*t>Dl6nYTZ3cRdVC4$*7c@3r|+T9P8cc*v_rOJ#&%t zrwW0-r=uZ{*xg3CWJA?R*>vcIOZzIXh`r4gPsXyYzFmy()5>I`yI|C28VXx$)=dzt ztXPVv32@+6_o7ctm)SnpY8XnXjTzHeW2o7lU}iUMa`-*&&%l1UX_OPU+bg|!^T+G? zRkPXdCiTieYWn=P_7ZosNebqy)VQp^(5=-=IIF;Y_Aff_Q&GX;sUtl!(A7GLyYNBI zI*qODtxAY+VumrXr$rN@n z-|z?bSG4Q`#`3Rj6g)Cp`pSdYgOoLYL(Kd(PA`$K*3>I$O~o}izc$h1y~}+3nMh-x z+-gs}t?1g!jSBvMs{nUJ$>GyrdP9`!jJElR9t6csz46Aebb%=yXr05j`Y5& z?5~1+x8wtT&sVsYjwpl$iuwM0>ZUU>%CIVoD%DD#mv%=Nb@rPV_h|CCZjdhAcm`bi zF?48U>e6ppGK;Lwg9@%!iuY0Kn5;u`T8o?wQ)e5ZHeNJQHe~pnpZUNF_0g1;sT8iK z_>AzHQ<`huf$N&+=`gn%(ON5sP@W)SUKt0o| zO=Zm4%5UOlJvC{N!^L5fJ2Qg;W=}{Y1ke~=ko8{qo?59~uPee};PA7t z(O(-eV}NmM(1u^ZV$)&_C8ednrNNno8&F-*UcYgAa8Iy61H+6!{Z!1V1716vTj5Pz z%;ah%c?>RmYcNwX%(XR^Tbw8SRN$Q123g8#nC)}D;Wt9NW~-nzE_zJfIkyRY-;c@?pn5L#YucT=rnDiKk)6`I%m~*zS$6 zvg!G3MTMl$#mCqio8i8te9?-jbwwx&t!A2`37;(2swkXWVnuaUwzPHSudGk9@$7`$ zeNsG-xVE!G(jO;-a07K1PY$5p+?2oX+8(eOsg4I{T8*|ZsMWbw+;!_GV#L!meE9l$ZeyL-O#4ljzG&-9K+!#m9_o-`@)*olIneh- zU+l#>ww!VqaC>mw1W-6xE?}Hxu+?fnsNGni0czZoI^6*)jBwFIyKBT>#4JQP0FZKC z8K99!&WIw`s*hph%g4in53%g!F!11a0MyZJ7{M}y{!u(|_)?MpkQVa(rNT{WTP4LQ z2*#c)und0VGb5?mg8KpqQi(MFg2Uf13PrUS|Yr$?|-Cv2f z?AdP9Q~xw`S=<1h(fmHkp0UT4C%S9R-3kmOW+}fQ0I$fht5r060e% z)KQUi4xk26R>_kDJfAJC=7J|iAGq~sSoY@Vq_S?}8x=b-h>BY%OF;2?qjQa-y*mpm zv3w;DtYtCod`4@bK&p+vKYB9Yr8Am9mZ|7h{T2Ag=-%|8iVx-kptbE22|bxbPMa=h zJwA=v??keYYX|CCJt~9MKM=}VJPgK`?7gkyfU4!)X*I=AV0TdzR(6#;(@suP*a)WG z#n$Ht4~;<(z6klYT~yCD3JtnHu8$n*IzKZ6Bu%^8?A`9ks%tP2ON0E518v;r^;UZ?c4^ypbe%vW8a$(=v~quGVk6gcQLUoV2cFn@lEEM{4>kVYX$MHB z$zaRcL)AS9DPRLcgrf=6ZX02f(5@%2YFKW(p zA=+iDzSXm=J;?5}D_zPkFnKh#$_b6ltJs~N@Pgx4{0Y~{aBmVabf4R{e}yY7`7XLv z#q*c{{y0&Hn3{Xx&+N0^xS4_7!46*R+Z~>fl`Y^yU)|0G(=Am@?=Ey9eD)WLfHXPC z%5x*;ld6ma${K?T4u|e`!_zaAuDWhG_ocDyZ#P&1A~APaX6+!Suw9jN+SC-*FMBpS zFMsm8|J5hr?{V*{c2qnj^N{onEFS$@UGE6NzC7UOkwWo$TokzsIQar##(rrQ!7|!#O7GFkU419JTbkL?^fm?Ro6pg*63|kADi#5kDn!lUG z;I|uSWqNM?LTQZj5nT{K+GxnFsQ0W8lT6A_#)2i>b{b?4BG5Z|u4&rmEvKTiR7dyQ zT!W~=t7+1&U!Poy*~d%?&P>PKJsPqrHQ257J58?@FJ!8aPu0Etj-Ih>_ld+?U9Z); z;86xxskB0t{>Pe+sZ|@f5&KPI&Af%mK`(CJjq6gC5HSGavvaogzMf@eZqnmlK6O$W zZvSv<6Y!1l@#@v78wA6lFBS5a!hN^z5wdPix;Y-@po-ec7t*gd8eimSGdg9uFf+#X zI+VgZ42Rz8R4J1t#H+g$Wj6CoMztxe;EArPzB^c0*A`ejS$s3&YOS<;*;LR{F<#p( z;(q5xEZJ96?2cyRarC&+zSXV|)lb4*AWqwTm71OpRz2T7Z}&aGc9{h8MXR>-eZFn3 zI<9+4L#SsJX!8AFSGB*HBG6f6Sw~wKJGkX9yI1+uqX`0ilE9M=tJBpVGMl`4W#+5E zpzBkrHLGU%B^FHi%e=sO>0g_HUb2L`0SZaHPXp83+sSZnIB3^GlJ)dw0k2=dorDhZ z<;FXfLU#rH(`5I)pYH@Z1-e#8@jkjw>*%_E?lA9|Qb9$BOt5T4!Mmak_H{l-mnS7b ztGjbtbP+d5eVg9=E1RVi9CbHSN`Gg%;t#f}y1q-;1g*7!;${e)qw)6TmT}$~RGqo( z{!Tj(4fc}!8d&U7xw?UXQ+%6`N^~9I;EQ~dEsfQE$k0NmMAbUp3S2N^xX3*IUDrBS zgWL!f$KW>NGh}OtaIL(Z8+*bZ!Sqso&?DY9sCrgUqB^KJ*+gAgzp!L6QPElYV0*Hz z8wqq5BKy6RsRkxMY9>KX->OQrP`+fd304D1O$q+~dha~n=H&$zu;*Y`r)?=Zu2Ryz zs;~{@c>Qd)YYTu6SP>BM)Y*n6)O=d;os0)A%mG3(>6fCM^z0_B9W!XBc#L+Rl#8Es zz%-5s5F(JGGRCt7c0t=*)dT&HP|00Ip@bo3(C1WkZNDw7L!i4$7jPeZAWx z>UQe0T^9w>0Fq1@4MKhlc{K9{Ti-`k`8IJ`COY>%G?H3t1kuNsf_liPKbWc7?%RCG|*I@+NQObfX{Er%1+mF6jy(* zy1KJ0u^32``j)LWz}si93q}u%!Uz1)ycjh)ZG2OKcAr1Iyo?Pjvgg7)qVZm2BP{e4 z#~{GtnmF#X&7r4D>ldzmK(h~DErH~85^R32`mBsz5-U`9ZYd}`1*I|mxT)upUEG@~Zk@wex3k!HZz+Xq1>F#21YlX45L^K2C*wVoo%_p`-W!Dy3 zIHGT&6~8%c8MxBPmi{iU&wyW|qvc2+ZEb=KnKTVxjaFVeEtk#E!}<=`)5oKpf|p7t zq*H1wlGh|xcS}JdO9e^-xg*|{K>RR_#0iT^_?2?wxW4-WB z)^yB$_t|m(W`p29e=KGyvYv;(iY>PcxPA@?>g2rpJvN%2L&1{|rf3*Sw;tMeppbW^ z?@~58pRY-Kh;uEpGMI)vosn-!B72{1Yn5efy;*!zeoHg8FQ0`76~P2TYqxoowFu$a zNsU!uAZM+`oPOa(T|mOkh3Sn?%s-!YC%U3_LTUrsO;plu&+x6FI&VXnSw!^nS}465 z$k0493&ZPuRrC$EAK{x(Q+tl>N?ZtcCo97cT!y=~{QZot^FW~$uE7aufNe|*XL%t< zEn17&Xv!^<782nL3xCDcgkLJwqMog#Eu)Y&Y0WTL%g8KfY3d8?uuh>_kV<^9_p)xi zltR9(4MHO}VpAAM6>slJ{!lHPK~q8JzA>$}eVuycO`%YMUWXu9GnQV*LW2<^D;u#s7y3rl281tq_D6$Qg2)-vh?cFR2< z_stcJ!c@)~;rGm6_S3iv>*x5=^~KMkxe=43hHBQwVBm&>Ki%!NwBlR>A5jmWWE=Ze zcPnmz-GlDenaZn4kj6^1nRU@ z47K<0K^HPXKaCF7X4vH2+HvR@m*&(UlkZrTDQZ>OmT{$!a0MvNc&?AtyS;!2r( zNPV#2yf?oau#G@`E|C^I&{q^rz5@#`mS`D3*k<)tPtaV^j}0WyW7a`VS@ zKs!j*xbz`)TBs!@%xg%iD31pL*C%{nI3~*`o6NTvNPR~rm-o?IY1u|=Y5@I|uN!Wr z9HwOE+T9fLQD}+ab5Xv-Zj+&N)u1PHHSX$Nc-#+$AZtQ4=XYJ9kwHbcL20YskB1V< z0p2OK&L+Bg5_h>}f~nPT%cVw=I8O5h1bf=mOdvTflYgos-eri7=`$>Zo};&Px8t>&0xg1^e~!?Nn?u z4X@YEcuv80@Antggh|mSYlR3(UGslFDzVu=@zugtc}~w~XDWEN&VThf>e9vr9Q4{I z=aw3dv)t}frssSC`l2nYaVyb%ePFeW2>}!?OmKOR`#xF@k9~A`YDkgAAZL7|cd^jJ z=i|8E*6{767)7c3k2l^`TyT#h+z(JXbH5=~7|`^nato>rXe2D_67I04j!?Ds2B}b;d0Hdgk*CJV#BNO-p`s0HoSzgZjc4W#%Us-t2tMBU|?~u z@=ZCz)7j;$o~V7s9o~8B)bhb!=)nb*?gLc=v3a2gdzV^s+W_rG{h}p*d-8-5MZyk?fV+aNj<+ZOV}8<0ri5!FO7f4G z@$ZF_P69yyN;j&b_KLqJy`%>iCnceL|DKEwXv)&9cD(sdpb)z;`T=O>+uHnT7(3e7 z1&cdCMBzWw{GW}QSpmN?$C>nZ^tu^U+_@!025SV`dY8AQ^oeXpxPZmHDig3NK3#Al z^ea!?ey%+6z@J1m5RU)5Zry4^WAhvMXG_16d)f9hUQ|RHF<=O&Dm4FHg`C8^I3&(p zNOdXVPb)AWCb;gxr?j+xMIs+=lRAB!Agt;Ab)y4a4%@VUHxPG4>pi8rn2syNbjJbp zy2M^-RLd>P?|Ury=cU&lmt!gi>hSl0x?4rgm!d~IUZM@Cf-~qi{StPZrm>8;PyaAn z`oOImmxugV1s}>>)CZt2OK2=ZqVp79$%Rw2G%nn)jiV+MfQ5HI5FtgPpB!Gdvo)ip zsvsG~M!52-ql&BxS2}M6r11QHQr7x5?uHNi#7TTSWK2??!w3Un z!78qWPzZnY)t@oIJ*;X4|5tn871iVt{i}!+DFIPBK}5+>O6W)^K~YdC5s|K;1S!&` zhOU4hz4s70{ zPhpfqe0klR|A)5!2Ok^?BK7~jX8xPB|5n<6bn_px`Tu}*j;6@BUcGE;IPgYgbxqhm zAbS{0-g4#c9g17muQHi^4$@<6OLRuD*e}cX zFS3VORCh^+B+}J8qfUqF`GYOLF^kyk2AMam4Y%tyi?qh?4t*j2vMthQSmKH+d5bHl zIl{2`92H!q%2}C(U@ihLf_$m|8H%OICD-T77b3R$=hl^;W+q+vJQ_I{sNH^z=LX|L zs@J#Ea{QhukpDB>0NueB*tgdyNRSsYrjB6MQ%-~0^TltSGhbz7_`kv@mwTBK>C=pj zLUYjya#>6~VL+MrfC8kT;sp2~nMq|o9o->SF86&ad5gj5Jnv=xM2X`^lWWG$JzriK z*7=tb7RqE?bxu1MGcM>xAwe?lVS9Ak=W^}QbHe-zGx(y zb=7i54(nI?o}7&Q0i>j;EV=vN}D<4w(#HdL*U!&Mv zaG6H3pq`;@<5CwRHYOVCXDqU<=}?+$1GXe%yaC!G#X$N}j|kvuarNWXiw{y}bCmn6 z=$Xh5eueADbW*cJdCI$tVII#ut!KJ`UYPF*`K| z<{kz%>r>@15gu0K+&0dbOUp^@&*5B^gv%<62lBTWrbsyOoaD@r-rDocY~wcNTKXuL z76`~fk%mm9RFHloLL0=e1XtrA`6E?t?3cC=d|cw~Q%(N8LYGD&jhE~fW7n=Ai>q9K z0NH!$oM$-|psA~Ri(0vGm~0|_ioHKeTD*|nV&mn7hm=`S$?RlrLOpT!XRn~)h1ROz zHoDhH2KT%vVgNH^*RTQfIBpp!k5QNFw?WWpZXXP?)X&(~4VS#DpST2y*`jCqpQdp8 zoDE;5pRTv+fgyz^BtMMhY~|mEE|##0M*1;s%Ln&U5Icka^eLfP~uqo0^ce!wFq@?J2AY0QF(c zK+wy~j(8D98cN{}(+#IRy}P+A>HIb--Y?domKzUGh_(An`ay*u%wCIp7-V1^X58lE z3?=a6cLQP@pwW6nn7e#Z-gX)xxq~v=vNky)*{3)g9BpdVjck<-{1u1A@)ADw#tS+h z$RB4`L635i5eVb4dam-z^(FPct}A-4u7>F5n?1k1l5~j@! zp?e<((@>~d0=&V4Xifmpu&lb9p&k{)`Rs-6OgqzXKiml|Ydq~~uvB5{b34h^ZhS3U ztH1S&b@Di_Dqn)Cy;f&b>Cp*`d2PQWX0}0c={BKdR=UK&Kr1lHdaV@jzI>Bvot*I) zz;_3`0e$4)L$9IY!8eTk{><&h=;jDkt92o92~-!(CG~Og?C>GVef*)%jVB|I1H)bN z)4lWRkL+}a6t1EIPk~QCkOQ#%Wh%mH?K>jERm{+1oMe|u$+cZ03M=I(BiXtT{)i1a z1ctM%0a4+W8I3c@;f-(uJC_i7z4eadSH28!x6&CN(?42gAk6i|tuo$rRps<)*RWm4 zMo|6>euq1J8(B!_l=4DSINW+kNa55Yvz}C>2bjc=D%y^gUERLTJF7UvJ3?K;%RmnAK4)&r*5h--*Taq0oj%4aM6+o{eL#Pfu=kdMR@)s8>P`I(*<;*Z)j3nl)e#w}0lA;h@QcZ!8>)y&cI6?z8PL zE~G2)Gj>*u?aiP3-eSCGJj!cV6}H2|MD2ZFV@mVzOZt}2#rb7CMe?*C<)AJ_Te{B?8(2Z)mC{^`FXPN~>vRR&I^^~$@gbcj%-Ab`LSbkIlD!gIAZlRKP+h+Y*7*sP z^QhbB;}dyFIJFX+)f@#%MdZpij$Ukw!BA8mNN(vJ995fi34Yh$g;1FIePocj&v&x%&Bw_viK>e;e5gxRfPP$(EX!O?>N~Au1Yd%)U9(W z;66X1%)1ye&K5ZtV~R)s;{B1hcdn=ELwPAKBH+?bw(ax8I-hs0VTd>EZS_XGsf0O2 z&0Up%+FcmwswKYQei?V(Ex6Nf@LZE!?Lf(C>5Ci=TcB1wU~aVYG7W|K7?DEImT*}X z={%t~FM~JC<-MyrR_`a(LGt2^8lm)SMlFTQoG>~=WC+5YvKfHH^poYPf-X1j;Okt& z^Q#hs6&AzV4H$IeSx@+cv_Y*=Bf=>a9sMU!;jcP7{G7zsQoSf%ZtQ-C4}O24rT!-u z#g1vZaPP!VbF|A7p!C5k$;0_7k}wi7NHk)*%7vcUmA~Bc0{5yoiSCo3w~h={fDiO6 z#kg4-J?> zBa|w6pp$iHrVvZrwH4>q=lK$3-Rv@#B~s>bvMxfzaUV8Zfo&}k+F-OoiT$!Z9V&;K9hu}0QfEEv?1_t?nAlgg-!(g&WR9020%lv@5eQAS{q-)+ zooT||!#sCN48?GNEv7hcQ^%+u0pq_la>yMCgC!Tb6}_Rhi!bCoWv{;I{B74)40@RB zh~?(ZIMSC`DYG+DP4_zD-UAvCw#>`5DLIZPYy{(|E9@hEq zw6E{suq{NKGw5>B36(}IhHD^(&(y#SaWCpmR^NF^d*b&yUjnbJB?Yc@G6z1^F0--N z=kbv@CK6^Y6`+4To>PaPPC8HOd%iO}y?Q@Bp>m{#yuJgx>>TQT=Yeck738YHf#Rq) z#ryB0C>*re?$Q)XCUsIO8F-g8c1+c=Ti@DdBlpzJf-8D-rBqM(&`q0O_RK( zFzDiQWq9*J5Wf8&t-wur#&_I>Py8be*KkndA&!;cYAro-wDqR+v0H4q+9 z!=j;>cDk-86q}}dLQBZF zX!nCuG2&Yrw-cI<28}45>Med^yH@Tl%->LG3FUFq6zT?z{Q^M6e*tQ8W_}JPO8Ha> zbnFH`T3a~~XZl+X{vM_<8<$nH=M+!%=Gh&q;I~>=@6oG!ywtks(zHW)thAUcfk_D* zZrRmLnQ*CBv*JK+*?P|>F+j@y-sN{Bi-Ji+mQm&xVyRq2GgkdY#Utdw&g-`s zk-fdVD`R)i36(l$tM}G452urJx82}465l=dIhenp)W-Uahjm_u%G_9=d&aeT!!ttm z(J%JR!P5tDan(YRr|64o6m@K6=;a2=&&sUE+dd1A)ks4Cenw5T1cn#z9oLb-y7m|w z_W4ylw>g`66I{x7*IHbA+ZAR5yGpf~&ZMu%y*$6cKhGh#PI(r~a_8;hnDQLPx`te^ zB(^JiPS)y8w05tJJ}AqavBN9+3&xvR0z(yh&?8NwoYg4+Czfi$PeC=q&vP_Gecdt3 z^?iyzvJr(G%qrP{l7s62$R(>oe$*8EE$wkL3*k{C`J&1JcyTyW?Odmr1o)Km;DS=I(w2%6$xrmcEKg4#$<}arORcs~(sf z+QLr*j>)5#zR>{O1sU!(?z z$z}>G1v7BrXZX zuf&h2uhlXtc56Z~1|=4n_>NIE2kdl{=Yw~;1SVqV*+3>BA?I%`zZM8< z)tB=RoN7-t%P{zTV^ptA!s;aF+l%B#OqZFvW=V`&wcrzNJgJCsZMR$x$^8L{rwIqw+d=X4oT>7ONZaZrbD_an9+TFG5zBdTtKB z9=QP%+AQ2rJ>!{m?Z5T_jAN8vJg_uR-C5Ozknj@=eDsCPpf(;GOXQmw+u`0h0J`p8 zXYqiK0a)M1SnW4G&sH@y(D$UIoo(^j|5Y(EZErY#9w2j&dT_2E$S+`*E6Cr34;?@x zl(LK(ug1Z41>LF~%8WAw<6#yk<8DaZ2)YFsCyUbK4#D?jQlyu<35;m>if+Cqe}?Y! zJVD8-aDSGjn`zW8UgUK#ktje$e zXL5LlOeS6x?5z_`UzKs;KF%#Yn@~a$qZf6zVk=T@*C@&}5L*Qa(uRCwcV9uTQo|0# z-cUI+7~OmHfR*QB?8AoRDDagYwj%L3Ueft`HgC&w(A~~Q9BRs*S;pK3Z&MvvhmGB@ z1E%jt?k%jegc^7WH&)6$fM6fAeU}U4M=J_L0{puzG^B}}Mc{_a=AegZXJsXAtzBYi zZQmw%6r8@dn~9bPJIJ*n9QI#^g+CRB)T?e4PUv>Yv1U8XuyP2vRrq^-6Jz#fIq$CG z+n!=lw|01df5wo$r~Ptzvwd;GYVyK}ZV;zZ@ACuWy&HP|K6v>PCHJNf`cH5)s^{~` zflB8D@qLp94$aEPS3rA;DBxtQe>vdJ3G`%WmeBwSMouAUMpd zKXrqiO|FAmzL|Y)lqub%Tlm9?dRv@xcvo55mk1}~wp5SW4~l4#zC7?;NP?~0Y8;do&F+)fZT~YK>Jz?8YQ!C*_v0m3b8sH~L(vvcF z+j+s4x3xMLb{Uxv9b#ePYtjs%1D<%cXXjj@Eq>~0Dd?cqnmsm^CN!FJKyZ=iF=d%{ zx`ATg&W2g?oy0zMw@c36F?Awse6g5`vzI`+f7>5ajw>6Suf^oO+%_Pi-1FwhJt`K? zCev<{PJIJi_&mag;i;~UU@rsPj}V(+_mM}q6_#|@^V36DRfqfDR4})qVeP+KpQL{~ z6MlAcSWcHAx2v9CyN^0WB}4C|`WC!$y{+YiOKuFWA-)GhW-XkDxf_olH50BjLE1s+ z21@??RO7H>DVl^{tSe?nnLVTU-BoGq{&1svf!7jllX6!ZclY?$$Y5uu>@~lrns2o6 z{2AC(Gz?AecfXB(rqvuS$LgPN>h1u~-B6gZ1Qhw`)3V;@&$4IwI*2Ax2fkl9_yNnf zdxz|hDRS=(R59Qcig#|^eN;9;lD|UuEo|)S)llx(LkAh2; z+x*UMdH7Mnuer{W@l#oyN+(tC1jV=j2mzb$pFc-v_dF2gp+<5tslYDy0?= z&hC((9}fChIE3Br2hur}ZupDV1+kEoE>OW#wp|(6qe1aVNf4l#Vo(2)L&rw;1;pGb z+@fobSupg0YD;~u-S-1PYbW!n26HIUD48X#w&us)QB#WTu{li{UuoK}nq-ewKflXj zF-iArx2-W)pC{stgz*;yV-myr59O{HWUmLMgDvYPbI4!m4xuC<$9Jj)Vx|jcXPiiE z+olVb9H*5!b)R&>vs6L7KNNY1s*w-hCi%E=D?axxueqM}oK=Ltu>-h!`-=qwxh4b@ zq!_pC?DH|eCrF0ybzwZ2rsVFAVGDCp7J!OuT8$JSY^X&I16(}%eEr~j+DetNadGl8 zO+M$3M~QP}YwQh4zF!YZ16d=?X|njz&8Swtg_1C@0I=Jw{f}SvD1@GJD((1btO<+m z{yIyAWPKM^>P#B|0Xb%sh4V)Dqp8R%@5vmA^>2rmMa0;9Zj0$DcaDJF+Rav0gl%M) zz2#<*2Dj(Uf?#gMAHc&7b7jc+WXai|>Xy2% zx?6R;GyT@JSTRTj4U5nopafU!*yiZ~(Q(q>cQ+|)=3iA$e;<0mE3aC9$Y|j^s&|8H z24&WSo&0L@G%|4cl~vBc7q(D@MW|Rs8%OPpuRl8=pl^S{ zL>!-uJUFwRI5BD{bhmE&%IWmMU03nOr-eSpK~y!cu^^c^%{5UxNQ=3C;n+cyb6FcE$tWR+^%o<>iT?&WrcmaRg0 zBN%D<)SDL&AriqM`>~bEeq))v$*H>fqg#k>;;wStLfB!_yK8Jx!l~nqhk`1<}sSlQdz^q#p8kc>S z$B0XhxRQwW&lPB@D~Q5tX?<~gLwhc5IVjCjU8OQcRz~Dsj>S->?J=JYD zrlFDh(wR?=X=#6isY@cO1Ldyqm+Q(V<@x*CpX#lhih6cPuhmcqX-^n%nnF!Z?v3qJ zEz9EAYK>oxQ97oKJOtw$=wx&EUp{`d@QiagBnM0VY}QS^GNRzn4Z1IjNE6@WLnF+R z@||C|tA5egBB-ZH9jyek(9L^x>pL2IDd*j~t1ZSAEJ+=b!5f_)P>HrmKqP2^zwCVw z{Cj67UQ$VEE}%A+o!XFa;p&#Ip)zL{@22-peC7 z5*T3V>g2{4%@E$B2E)b;2MOxUtFH{uX{3C_nPS!>an%q-yN< z0c%57Z`kd8GiKOUGjK{!S`Qh7u1$iZcZwa)g!1N9eNJCorv2h-^E*hT5xPOTznGF# z4}3fSyX8ASUBE(`2Bh|c#;#dWXdZZY@uSb(vKBme}R?= zx&HNpJCQG+Y{(dTxv1jxN;QB6_%`$kIMJ@m85IywRM9Se&A--mLKu3!oi{+iTMZL0 z%WgTP6Hgq43u?XNcd<9gYs<;ZF1&JumgB|K$8ShrgsBM;h;Pu)XBR%l11-NqRy?Dfm!g%Jpo8sf|7sB8*+%efc2by zM>2hoQm8B?BIknIz>YTiZK~-B#kZS^)+RjWp?^#n=V-tUvzJPL5NY&_T0&(+Cee#m zjX&iz&D@G^!+Gp;c)J(#T6l$*#tNn6&K7RDW(f*-PyA$w9r%?U91tc_8rIdpL0)z- zit{>YR{8K9S>h0}Ty3<0K*q47?N$!f4{GxeNqiJ7_KBsKw|54w&2$ouOIg0|+3 z#-zu@a*P=ybYZ5^xoo3n!ub1kpW01M%DP0A&`4zSF}3okl$`^&lZ#u}Be(@CVK8Ni zq3d^`)WcSrJ~HzOUpA4CV8e;h%TA2rC^$ahrxL^lF4d0Kv3(PI!M)}K+H~LCFZK-9?RQL+`fyaW#@yK=t_#-#bndbBg0}11WaB|;XEe~A zBqyUe!3l6H9-j`)JOS82WQb=WXHJHBk=NUJzg({%fo;@-uUb1}B6egW_B|V`ko^zQ z^%ja%@gn01mv3VpY`X;l{wfWzH>G_(!Jh0#FuJJ z$;fZsi=x9SY`S$!@9wMHSKE2pK|sFIJo&|E`8kp8iucZa0gG?8pXjZ%httily)fTz zQBQ*qquw$Lmrk;dFWoLTzPjh$7Lqm?3qTj?dffXN{$`u>3Q{+{#dhrvE*!Oz6+-io zu6;74(?_}N-m5f8^qNx4QR6G15}Y zfF4$p`&|i0$?U}h|TW_ zW^EPu?w3vjffEm?Jov1OAm1HXkxAR_V09AA)E)W-W0ALsG)Mw*8|$Hk7?rX=je9CHy%j7 zpdcq5#~LODVtM2}m!IS02E}6V8FBeW+LkGI%Vfu+y^+}WN`(Vq)f0Zm1UfNVlbt8{ z7pEt{R{llrCsTh*5kX9motmj(M^%iU77qOr{i))ZGd?G%NuXkw3m;0e06-HTEYz&{ zAAh2-l1McHEa}c}s0PwIPl%v@^IMAhTfADFo@G}$o7mAuDRspKM%{}-H&z5dNKmvQuY;)gFNeaSI#{A^*|B6 z=<)MU2x${kTfOFuiW`rHRV=;$8XCh*L5b!J4v(L=%@_|5an(UD;d(G2YcGg1rdI@n zyWciQ1oG<~2FoKSi%ku!-a;eZiGuTS`SJLqIM@i5&qwF)UCPu;*%Q66*o*E$5(TB+ zb5#t~pxvb+^dOmN{2P_im4Jjs7pt?m#NwMdwWcKdmIY3>e5*?=-ec8FsYR-xjc|$fqv@8qvxs57Pd}h=D@YMUMUl)!J)Zq zn(6(r7MV=6rtd*=zg7*}DTy4QMW)wEoyKeUnv-vI>wr;*hcjF5p+819GC8|Q*xM$k zxoWB`0c?k**7o^+<1u+5uL4j&D)EQQ4q&HFK*3pLWccrvhg`_T$4hr~w_d)d^51YM zCM>gX+QFS?>EM!+8P)bf~C2jpq#j% zAfcRtt%n*Z4k3^lgfuIu;Jo`Q>(7L(sMyZ6?%R@jPMEk_lHSI4)` z@Jc?40|>v!U^YdVF)e#LW|(sZ#@S5{$nMj*a0RVw=ixC^-*DKwrL z+l0g}{|7iQBkUORQ7FC}o;gPN$K_bj8^*rMFaByD{1!lA5D&yzUF+@UFf9u>Gd&BYFC*+Mu;SN)Fzk_ENqDk4mDtyZ>c-)N7F2cOjhliFXjS!RD zN3?X~bTvr)GD~XD<%n61_3J=8YyJE)$>m`T=1y!Np zYeo%e?@?ky$~T5-!awts!b*@dF~SuM2p58ugtO8y3mUN0Nq71XmWEJaM;^xMo*AfZW zh7JB6YiKu}dmAe^>GFqN;FL%cBfl2SGtr2A&Zq0p63e(pZ(2Lep`-86yKk|0-*%yz z80-ePa|l@QVYod7XhrcFYpKQvn%;-E zsxd;JjALayPV*kL9>Z^OAB##_fxCclhGzEB>4{c*boE6Weo|v}QTh(;x;Xo`&b&pN z%bTS&MDiBe=}%r(p1<~M;MeS3okb-dTLGW@B^yeCqVbNEJb1#V&Cdc~ReM~q2Amw7 zZ@~B-E9GCk+oLXgLkXeAH=P`Pp}XGQbagkkZCi$R!A@^N2H&SxGr8fx@IHY}5bRJJ z1ykWwwjphG2BCAAi*zBVfqVXl!MXh4LkJTTAch1V&xehPn1|8rO$U+C?F52O^!rwg zTK>VZTcrBqDOhW_j~rsOU&0nm6J(_y&KCX}_t_TNA=CvQeE=b8P&5dMqz|`-KLd$1 zC>WA3SBxp%FP{iYjD?sVb;!wxP9cOwbb&ZM&ccYm0fzm%7N15O=MYIgihH1!07GKP zac*HTEeEb~t`!rUw7_8Q@*!zGOlByZ2=x?|6NW};mJod|>lDi^*-9X|6a-@z4ahVy z*uGkg25+>-Xi2IZm3pwWTB979=fF}8XCc_T?>ifW>CS{5wCL6_8$ou^WFN-1}!fgaD$+I9=A>wI2Gtt`+Q--J? zf*B%OLRzwGf?WJoWIRNFV#P>F;!%dPQ6+cc;Ka!Cne(;tck|2gU1p$bp_VgFMLv@h zC6N6#aA0>Qf5d#GdSq`-`6*qK`=DSz`C&w9!x*AnqL++$pe zkH3;VLOimcR0IWk6?#QUbOg&8H1h-us|MVoM6n6XTbM+#Ls^mwb7%{#i!yW8bK47M zj~Z?XJviF^I)3O#^9uWlz2^*1IZgkzfMYT>SFuC7_S2@R%?=9IX8k)-}K(0crYZ^BplL^m(*FvhTxh|*ef6lc|+fw9| z4FZLr1@>++?tg3eHUV0>3-fUC&}zqR|JJ_lLF=*p)bfP?MERt6OMMq}+=!(I)c~ax z$Q4Ki_58^O+Y8%(BZymw&1gGzg-A8DjTP4d(};qJhI!4}oua~k6q#9046^>|Err`|E+@)(!N#~Y@))t)}$9s&KD<)8gO7iT8E zE!QqN#Oy&(m!geQjan2aCnz`mYQCd5Ia**|aJ8mjp}~n8w@7znxv)O7QnWr;U08Kl z!dkIE3qQ3uJzt$YgE%8!R$bP0CFH2ab--KT)Up@CD$ny}42;@DxDaDYK?OP{J*p<2}V z%QfVl<3f{$TLsq=`J@f8n|#SMc@jB0rbDOWW5=`i`>o@j(Yw{7MTQxhe7oNZak4M8 zukLT@ud5$^fMbEDe^~iI`@!8`**_+*JOGk^H6W`FZ;Aa>`&6~Nvb%OGu=|3&BLgb~ zg-A`zP0Uz>+i^+ zS^4_nzT#iP&hA1T8OJY9OtSzC{Anu&*eC@}JCLb8=R~_J$ z#Eb)RZ)*{g*bD4T^tp<6i%Z6zldY0d$EPdQD-0AW6}8EJ64%aJVexo4#LA;Dz$*Ga zi#;QLM0vD2<2eu%tU&A`M9F_j?>R4=Kc{%uqyb*n`Bvp0r__^FiiK1B`GxNoKm2^|Q?5p$WPove} zVO|++xqOH|bF9>-XuOp8Xi%ss=srI(zCCuKW>Usejw|97(PccfQIsF1m_IPR)fXP2 z&mKtmG6ixQ-bfjl?2i?ytJLj}TV&($cn;zoXRNVzqP3#a)4y5T9Ip+y7v7jmdR{9& z$TE|qk=4d)-n{PHYWdM4(VM9scG!*gR22lwL>N~Z?-^%fTw!Rxxc5z>@!r!;Xsp%t zSq_I3^cBPxu%>*ZTdmPDb)UW_rxvLw)*RMSZ@I2gMp4nGxTn}`N^8-mX{jJ?`Kcdu zmsViOQ-xO1E{~wxO%`GG1+K?n1N6 z4SpcEv1hjqphrG``?J1A@T>C(?1b;EilwuXbBIk%BXmx>;-nH|zL7bfxgI~9Yi{mv z(e1K&bo)${CRz!9p10*U=G{`8q4!`!^nFHb)*(l#_qF(JP@!j$NOp@4$D7k=YBA%% zWJo5rbF}yQgZul9DBXh&n^T-~&0G9pL5s=ceqg8jn@e?nS;2Yj{ay$)k1DpNXajyL zw%gtBU$K^^RgP7-OXq6DO^~uAUd;9kNZjPkhh(kAexI z%AK4q9Yy11XNP&QqT?|r1e=~czSq|!FGmM|+$RqwN3zp-tIq9C*k-U6-1qu_V4Tqx z(0jH@yiGkCu2Vm&cD`ZVnmXPep8r{_UG%m^Z)f&seWj1-O!L_B!0#CKU3~7i4V(_e zBTy%p^p<$NZF9XgojC}n|E!DMDdOF}nXf>?!nm+-mhI#|M}zNFTE;kQE0+p=$!NOKtbAT z8dp{{j8A_kgF?N%ZS^NbG{C!33cS7BmA$=n8@lxLFWdxv9`h#&1abEkHqoa(rfzM9 zmGZvQ4H8`NVfjNpT)olH491=#Gf$)g)^F#|HZdq`-lj2OQ_$ zX%TP=Fo=KNe*gmuH3x(IpE5GQ^Y14Xxc}Dq*ApT>1PmJZ^#!=yaz6Z@(hyKN5dS9) zRtn^S@hb|7ivv$ZLkDAH8%Hx+r#NU=7~lo0otU~K7#KR)-y2+9f%Fn+f5BWy%}GsK zio?*>noi%y*1(v~&D!p7J78RH96-|A*h!zz&DzSwk;9Ff_@5FSK>F`%dSb$Via1$v z6RSzf5enKm7!$J4f#^WQJaB}Bgj^0rCL9VvBL7nz_{B|Z=Hz6@K~L}M>PqLzNN4L{ zO3%Q~&Q1^dM*r;_El`5i(cQ*L-;LJBk>p>E{GWD&j2#Uf%+Hl$ zO#HW_e}DcpPGdLo|MXO_GW~bW|H}ElYbrY$I|$lZ16?}t{AaoTr}BSi{!c|N z`oA;(FG~E2%>TRv=*$DhMgQ+L;OPB`PK@5Ddoe-FQ1vnE6kiK>u5W;F>|*JN*%q z-@bhT@fXlWtxk*J&SqpYJ14lJ{B?of6XuAqq&VQ#J*+7sY3EsXKiF6Yw}XquI+VaUl@DQX*H%AtXGTkf76t2EidS?%tN2qpooz|{G}b* zKb`N!@G1xhe8_a&aF2f7mbK!FA~sBS6IM)QOp(EJo3X^S&AN)pGdsMMqN1b7&C_%+ z-!12^KX^vb5f(7=!jZ6gC~Y)K(tXQfQ1WnynzVa^isfx_K3OQ}4MkMc*UvSsuQ41{ zm@Sk(x*8-Xf`I<47IL#Uy}j@>x3kl7yV?9~zJB8A{V~)R43cAG!EYtXO2|_cH&^)H z29D6g4HA#h1R)BaJ1v=3Q)G7_s;+%%JdHU|RmVM;Njh(ou669s?K;ZoW+(5u>!x?T z;o#?%y9Lelc5B~`vxZf(%dPIcnb*r+#H0564eA3Ob=(Y_`w<1%M2Nq9F!A+615DsW z$8&UiLW204o(E2T$Ojn=sDAg+;(W8B!F&nXSAH^fch|#P zRX$OPGvxX+x_tM<$ZK-TMFN%*FJsj8AB?F*Ra0d)>LNz3Kg*^irjuFqwwpZIJYG$@ z%fouS+}xReE;g&6wl;`=SuHDkiWSCG>QkozsGlE7;G;I~r{y@#yb*2^kqr~b9|(nI zu3V-fJwDZOGr@WLbW(+Lo^deyL)&ye=NRkofxh-V824g~P0Gg~GPK)_<~=4FA`bHYRP z{~Y}hLR5q%_2xftkw>M~eN=?dp|BdJAFpuip_hpyBl+^eBT8$fe z_FToho4dYqdJEd;5aJNR=N_ChsM|E#yYNkJzF5AuhF1xV3gHbGbQ9ro)<#(|=l4*0 z;c52EgA`JWw>8+GhvIOiAc6`$9o3xCbYc#jPOF&g>sRc(=L6pNl9hbh&5n@t&CH%a zJwH%aj0D9i`BW1U)WWOu`={d59>OUWQMVHoXq;+(0zQJ`Y975%&=#dNGmQ0Ajz4Xn zoNN*=N)O)%a?+RFPftw|U!VhdPfCTqia;ufwgz{TXM6oLL2#1}mH({6oAc0Uv#!y6 zcQ`G`U56EiJb*IGw?>F?nTIOo3(HIs-8C)RtVrNJL$E+DpR&C1O$F>{EUkeOQN0Qo<-#Fq~yqeQ} zJg0qeJkg<0yE!qIe+}quw7FXx6iL#z&-s*r{eFZ^RXPK!#iJSP05ca*C?G z4UegL&TbXz&lgpw_#=**@QtGqc&q z{$eUu@VhC=QL1ScWlHjLw?!2H&Hi|a_wyg8!zm(-b`NGW)TT<5_{=#Y>%`MU(z{D1 zaqivSUFEfx7caCvpm>=|MVR#KTnL!Dx_V+t$^qqY?7A9eY&j#GQ>*W^zypK$UZe1zk8voyYOZ5df7uS5> zUzv=CkPJLs@s^#gp%zUX@pUGm5;uo94`C9XNKkU1-8tCT}yu-swblD1|O5e0W@)NAUrDrA8&x*g>SVz&O;Ley;LY%)Xm0+d;GKrB5PUuU|3sku-V3 zpLpj&W;Do7hKncT70K#f`p4@XCm1kXUvo`NJ~(&gp%pO6%w<)uw| zLgannL*%;TV|SJPoC^biH71+IrG6G)SVS8r!BR!CtfAgH!Tav@OLMVOyY;ZNta2t( zo9CkaUZ-Uzocb_=zC#ZFrGHi%bFM^DbpB+`agupw#71916&c1w*B>r0n_b zbO8l%qL9-SHZX-7LE-w=*6N!%9|aY2UUo)kjDIu+KSxL_WT-_QPug|+K|IpH1y$X< zc4t%2JghLLQ+k(e41HBqEiElw!6d!r`SYi0O(K$?VIkhm(JK{)DF>|6f3S=L9UAra z@XyepzEKlGyh9Us#Tv0vxF_7ttLnzJjia(IH`qU3?~!hwwv`9@XS`0`Pa~8rRzRWc zqfWngF36n^)GnU5zy_xJYVyAlSV_*OE9Yf!lOJ`Z87fuiaLu17^a^7lJQc%}&F>vS z^Hvtf+U8{HX5$~KUf_yC;9m$$!^(5P$4R!L6Z{O=6;^81reba<)GlZ;|D&VvIwq4c zXZ+P-YR(1~4WnSB!S_y{qepXHW!E9BOUzO|%H=IXx-5$akG_Q^`-_jQBuQo>6s%S@ zlEk||Dl50!{Kjx97d=MD_mh9E(n=;Tobc~kB)2_p-nR7VrnGv<-%Osi)Ic>AZ`S<{< zRGCoiZ)OUMs${Z=n|lgsAKxw(I>p~SJlU#`1RXn#)wT`=!SM9)OTT`B za`}Cr!(%&1%q5IEL}RmrM&tQ`(D2=rhSA*r!e)_qeSd{xrTSX*vQQCeRDSsA%&(J!J;Ot^ z`_fU0F`!KxaZ5V$$qIGbEKy$pok;|lx^^AHoOd}(c?JJ}_N*Y}shZcAuNsp&Dz#>E z$HPtpHvGpZYgcCb1B2D;m|%Xf)g+O{NCbDzp%9FsQsE(zeUm3`a&)K$8&xWy7n5E-6&S7H!e9vW5ZUn&e20zoEnhba%G~lcRhGjgQATf&qe9gw%y+KF8HtO z6l>jzYdyuEwN8U94zMIgqbfB|oymC?%@1`TIiCaG#esh9N%Gh;iS*Bsisssn)`jTG z>o+^s=Lu;gAYb=U=TYh9&&nfiN3EGm%l#6Qys@(AP*hP_h%EP5o6#>HC~@Fr=Bv~q zJzjVueG@LYJDD44T2He`^gpO05RB~@4hz0rY3?}~$DMigU(^iZGrqX#m9H(G4%<1J z_7)0JjRnD?M1r1N3OszQ7EB+Gu9g$U`=1Z;SWh(yY5tMQq) z?e`k8<9i$up@kqyI9^o^xtQwbjO5(hF8FX!A1h4gVG--tbt-Q`*&ISB=O8W-A>`*> zYJQ6Pr+{K<4cAA> zJ>N_>HkC%38eG_UVR6Dhhj(`H${1KLtp|C$su!rPEoaSdEuK-8MbAQ;Mc)FxWc$NF za9+=jo}VOau9W$ZZtC*nj1||h>lmXzealIr^%^%$d_Jk_)Ols$e!ZOy$;RvHqQM+< zKMr(Q7FjyA8Eo(D;oGc`f_hhZ9BCMQx~QYWr)pUr@99!~u7OQ8)-FGWxu*ifZeOsi zU?EIFTOC8Vz(SZKEmtaS++HbF4g9&lPH(~Vb-Lx{@%Ku@Y5oQi*tb7Dum#FBuh-=j zl7WXvO+w};?8%YM<~dfJV8-P3sQ2KcYFqB>t*T9PZ{0lU;e(n^X`7X|Z+hvSz`ocr zOl{Ea>6*0)@_by&2IQWH_sUB}w6V6)fOvMh*cqlPht_X%ZYo!0})R8jx|tRydL%G*9m%m=&5F}?_*L0xkjIuem^Gm@nC&-$LY zwfhf(_AmX{o~aRjfAmlEca5kU%a(48bQqERNGU4Z*s6~CV8N@yYoMOOhe=#64FQ8N zG)*uKJ<-9VR|K-V9&Gpq197veL7jl&3I)tmM>XfI`u+97chRX18tYPwU5M$@hDw6w z(#4O+Sw|c5)~mSLp13~aq6^-fcJ`MfrrT(5X5$-f7zp$2uGwOW{;RNu*S)Mxs2)t9 z2L@>hJUJtrH@>BL5OSQ|Ufa0_220$v^B-Zpo~=oOZMkd5s`UA~3>6v(Nd}TyIK|W; z|HgzE_w}gKh|Hd@)#1Um1&KXY-#0Hw7ZoY*W?+(}w&uL4?bQ+qi$uksFXula=oGer z#dg+5GJ95^hdsx<%|pxq{(`2U2@zgGRo{Q2GG8WuXLn{!i0gMn;4^RFtL%OZ!M+%- zG_m-N$aMi{m?*nI9G32*P}{lZf>;C}NN0#`QR7VVD_7S^k>>YVQZ+ie(n9} zhEPipt)#q*=LVeJON-hwzT8 z9K&@RSv~z4;ZuO{4adNRb+ahb(Sz1M481P9$dhN4KjGtSp3iCS%)TRtW;ci!;kFF| znDg$5BEfCMpQ~&;U!Bmey>=Y9y-AZge8OYZDQjx?UH|ik3f@`?by-LeTtUD zcLuYLgDlTT5OAE)ykr9=a5%Lk&h72#)2%Azkby1MWjcmdYl{CQFT*hfj!2lVaHD7S z*?|rQ*YkTLLU|XQH<))dKB2AEo^&G_3}T>uO10H$MJJi9ebUKicDn7g8G8xL z!$9;6s+r=1a^_fnzw*1OQPK$7{wPFY#xYvuC3abmX%q zVM%j>vKOO9f*pw1@kg>>;qrwn-w0g=FzcH;qUGrvJF?F2)(b@@E^N%P=bzt4&?$5u z6f{#j+$1JXzy1_IUOf6UYi;l+#gjA0KdX=z5cA&N#|`_nC4TPc5<9~eAv88A9c1sP zSqwDPMD$+h|czljW1yt}^GgvEoZQ!DhUPu4L6>*A*RB^V;WA^v{Pe9A~r!`6n(>(Ony?`nus zZQYC;Ha9qW>S4#U<;#<>`h=~aLpg#Ah?n?hBI-hhCt85Y#H#!jC%F9@G(xs#uaGV7&=Z>>-X3)0d zSGvbN8>Jhx!+u=6fQx`Oh;!h&^5{Q^1JxCkBGwcli$tOlN8uu^f2hAAL zIS^>lE1=X6-7zk1a%Xh*bXC5r-86dazh3<6kH9G7A+ycw$Jg!w+$bv6Nuxzm7A+;? zfU&Jq`R38mJ%%|%CpME}EJwXcaAH1ORGv9)I{ce`$)7AcA?G^Q3knh6>Nk6N0xbl+IvrMe*h@?A2-L=y?S&()4$IAFJtBT5yGwjk9hR2S)v_Cc~eb2Ha40di>tCpq&3W&1_Pb+@|U7 zFcWgu{^ggrs`uT0XDQf+IJv%*-bIOsQMCGHRyzF?I|)5$MmAwlKJeT;ks%-4I-@zX zHfz;5e{`RN$w3A2?xkOOw1lm7?(*g!#FW1H0;OX9`&j=+vSZR+l-6s?5*e;}-Xf?y zd@f=5b1rW&ZG};@mc#EdsM+X}kI?XDli?&@W}3(au3`lonPp`(zUduNEK;0$Mw1ZE zEyt}{$LgQiIO`ciGkvsvXbOF2-YvH8TtU_4PA1(1ushDA{o$pum;N3;JUf*{RjFoZ zxJWQ+oi!=ZIvIiq6ICA(D4#W{47NBouo3|iQ`6b+DCEWNH~T=V=iw0%j8D0@qOut9 z5YAodHa3+USZatQPCvGJ> zsV$Ya(eCLf5&e6Lm%UH&Gaq9%99>cH&1rggdQwt>jO+d~v$I5PhQ-*2;L4^QI0NKL z8KmUCy;kx~{$bnu^Y!EG45zUGaqoLlgwF4(%d5Mv}43Rr*V2cRL5~mdQGj` zc3N|dMrBCR;sUOrlF#9q4OXf4*Y<$hN(oPm3u%(2rLSzr1+m~&3oZ)!ALqY+EUXeN zaHfTnT;#jJE%ZduGX973P;)bsU{C}|3H;LdX;i#uOc^=%j` zp536Wp9ovWwkO~7W{Har`jY1e8OJHg+=g>}k7jWwJ&|C(vinXcn||#$Ik;Mf;5HbAV;Tkl+iJba z63_n{jz%3eZW7kmOPa+^=u-yRLE4y2p4W65eYOkhnNg-$S3jRGI(0ww2~!-Z%E&}> zOsmQ;BvU8)7tF~*UA1lawVJBGueG_?aa26qZ~Br`<|ToqM)Q2b<(8W4Nu<+Rq%$Qd zTUTF8;34jvOvirY9`Bsf(IYs;=&=DY( zBMb6J+NltsERg?j_O$(DFMZX4)IFmsRHwwh;DOe|iTmvJO{iIK1NRgis*RhprX4h# zBet;-GTOh|sxa;Xo0y_4MJG)1+_vZL z*GB^r$Cn3t0O`E`Eso{KUJfat6Js?S<4?NKib;M0{iJGE6V&CFG~hJ8B$`ZU}BWk^h~ael>K)J4e_l)F?^Ij z5*Q^g@Qt2$kr;gG*XMiH2>JCot!4)@=}e9!qd2((r{;;H#P~do-X+| zG2H)!ANB--1@XJpqe#I17ZwTjA^M9S$PShM3-o`3iT;;_VERb-XF&@83!C)I0{~6j z{)oQ6{@Q);ACRE7r)4O}{x`Px{}1`! zAn^ZB<$sgn{|mg#M$$t_OdOz5Z^pmcV4W8q|3#EK?C%aMMF)TBP^$PNV*Rh}c1nU& z4JuZtz@9Htoiv4YaBwhte|yF61$57lzw%@>nKsXUcfcJ1LiAs<3d~10AQxRNL%%m< znznIG0MK7@0rd+Cz4=S9jI?wC00Stb&}kQoL}8cm06R7cn%vNis(qd~9Y5p{uK_zX%bB$8GTr(^59)1FtMO2||_Br!B-~^umd|Q=0V;SrT zg;J>oSZ{zTNQSlM*#Z>-D-nXtcUioNJZvT(n;BC=X2XJV$1^@25&Av!FsjPPlx?^?L8w zz9(B-rC$TQ<1T84{I)~)=ulrizSz#hy;a1f+0;| zq>gbWlUdS$%}7lPxcyzuR)pQ%-RG+H1zYyhEQ>{B@Tm?V5V`nxUoHdLu|KZ*(0$(m zhve&G1ZRPd*QM`#zl!v4a5_@Biz#}2iN+AaPa>j0lv2V2z(?POg_>`6B3Li)=gMR% z4{yrnKHWWhuRPaX>@>avkF@nvon9^Pc&&d{ER(WdGIxL3(cSHWcU*fO6{Kjbd3&=` zb7WOrKb%aOxI@8j+rQo|K9}DQ68N5e2{lEc*{ybu7v*IUopDPRDCjlF*ZQHiI=SgV zqP6$-%Kyx^#)AZv1#YOflM3GvyQP5a4Yt`ivOd|Pj#+QDJcHSEoqp*MI6+<2Hgnne zeBP@4Lha#4VCCcDCF*__JWxlu(&m1{#6qAT?UdIUzs$7v62Tw*D; z)lt8u&=|~|u^2O`0D&-h$s(WU2P7cAB*a_cS5l(e+cv>_|aj^t+> z?W>l{?s^U)Qs0d%I7vtX`)~yoSciz&)Vuc1;#ZyN{f?dC5mg(Bt+b1R_<`?#R({iBTC{YGV4Jj^{}?MQu}PcoK!vY9IY> zhyN<7{b3tBkS7>J_w+wDF}S0>WR3D5oBLQaq(~O4bZt>JFJ3fAB&Iaj! zh{9wLI14BLO1&?M-sN*UEn1qjL+6`qB;8%2Sfm8PFD^SsqE?;3b6wMqKECVtx$90F zNsu)&VIV=a>8wue2C$R@upC~M3|rl8JH(xz46vE(w)o;Z8x1_r56Qg9xEbyY{7r-a z6E0H74v03=6W@9V0vg}n`?=Mg}b(BeQvJwe}B>k^xW!x-+h+EKcb5ImP9roY(jVZ*x1>MbkJe!4ABBM@rTG)$h|Hh{+)f`GMC z*>)}AI)G*BbQRdU;dybUqHc`l3W$Pc6j?3?Rw+h2Q9NfQ+LJDSh;>jef?~pL4DZ`v zme;ko>kv`sT$3!vQT_@#fH8YV0jAn}rSacrC5yEbbrUSt!`#poz%1LWXx#9y34r}7 z_wsyyksb033}f!BAVo)k^|$>v^CHCp=S5qc=hIQqZ1r-?yo%=dS@S{Gd6KG*y{1)- z@Ivd{g~@M>@X?(J`txXxV+vNcOTESg4a>b`lskdfzi6iG%hh0!e(98Hn%I`8*=u2{ z3AXF9aY{3(_!Fz$*l1s0a&2}@!Xc^DnF+}j%^)y(RZgZ^T`|A7&0Pmig(Nwjj{cRe6WLndm zPd$fDdu`&?Di9vFECKY%4Vn9MMUt2n3`+4@JxoL4f?mY5GR92v6^EuhPs;A#CGsYI@vHUkAo+D4#)( z$wwF{7vzr-&~uROTggT&3;J$Z*<2VH>jy1G3RwdLf>9>Ke*N)S4UW?@sO`F*bVd7K zC*i};#3TuV$(_75n+u67$!6f!`<YX@)`VdIYxJYhWfZ^l?iYQ3piy+>f_dUb=nega;D1y#!OJocT{&&r%1 zmsPpwdR+xWBm{W;q|cRIo~6>F=~RhA@$tdXvh&BWuAg4B?R+gf`@H`d9u8A*5RguE zyL3A${l?kef9%yDdjf;WBTbFbbC?9a`vRnBJXvk5;=CRr$?SCdz<&u)%nk_6$fBEB zdguToLNh>aB}E(NK!S8#SWLJ;n))dLjz&J1fgd661(AAw&A%r&FXoz|;ROl2F8wDU z08OB6f>}+->{c)L;|+UIrTEuWA&j5ZNQaHSNq;O8d0)SSAtON&Vh)wvW+TiD{|)eo*U)(-2Q<#el724gfOusm0+VsZ@%vrli+6UNn8dH zyqT6FrSM&5JnkvJtD9yrBZCX>4(nyKO=Jdsh7{GYB`$Ys%uLdeW|*-8P)#Ns{T6pTu|`S4l`ZO=7qL)h}eAPaL{$lK6HkC;9FnH zyu&B*4|doxn9j}#!bNm9S>wUAY6&5Y4)q*adpJ+%{*>JruwF*cv=z`#U)NYx%<|@F z(;H6n%`p2!bpv<@aQb@JGC$dNrfAwU3qiu2g~CMej_Cqgt`Eng=2GdI>CvTj)&Yr1 zJ5m`{fWPz3DUOsgw*eKSy{1?8l>iSW%^0Gsp-UY~A}-MkI8=frLLQYU7SrEwNR5Q- zY*)q(V`H70ZqlId1yX62Y@ zD~P-xH}wqKEGw+Cca~KvMU=@G!A*w8{+$_`Zo(iQ!7ILprKiUL@R1+JZ*dK{Mbm!- zIiHLZ&)!hL;HvD-v5)EMt36Y1jtS^2QznhYVkDfe$ z?|C}6PM>lLDv=BM2t%Ma#!p>dEf8fV_v`h=>v0_(OXn!U>V5!Ii9>?)1K;_wffeT} zmb`foT?|6KhY`^&1@gr5MZ>BM^U?MpLNwEsT9EG_hoG;-7kL^W#JuYSSvuS^)9gs;cWf zAMzBeooTV$8XD2ENtZFvRG!VS_XjDBZG{U6U@H`k<@P>ie3j zPKK1NAzY_+yHk0Wkz2Pf(MY=`D?UCtFJC_h<$(E9aY8XrUnxZ9kA>s+Y)@`7{4fZo zl#ujX@@P~R)eP=}n}Wizibbuc)y#6!Tsz-j==#GvlF}G{k}~+-kVK=u0C&|Do<6Uc z*v*u2hPl-g1iEUr-^;MZruP&YZU_z*FZmv!jr*IdM=z>XcmK!5)Qf>VayQ?o?j40Z zjQ7A6vL>(ZAmPOih!F#8ib(sFke{BhGapokOgUG`1Iqc^%B0es~)?Z^Z2S zsvh|o4`8e^3?<{-iL#plMrgg2+H_F*8KzQqf0f~oEA=3(uj|j^NOw*sZhuMn$M7`< z`lT8%_1I~{ABXp5o2OS(fZ-&&-VY-z0pD;Pv#TuZ{=2!6L&xmB*NaGmr@q6T6F$91rLbIpccUi0HJWBNTXN>Hp4fmSa-HHXd72Jpz7&3=mT((#PP@wxW@6V;v878#V2232OXu zis7r^Ha+b%qaKA|KDEKC3g!p!JuVLmeW(SK>T63*D3hciMB7cZl5Q#@l?bE_-xqBd zO>2n4>NQB=8WELt0+32QYz+6Ob20d6kjZwj#Pj@UgpZIP%u8U` zRJ|eLY8XHDXN7(yu0#_1H#`X$HUBPx3D8J>ddO9;s(Q`stof}qoDE>1>D#bh?-;>TqqUx90PnG(|lmx1|li$?(7A9Q~sueU6V?Y5Kyr(m2m=qsB zU{GLS+@Zj)p1LNzVbmJNYCrzMgXpBK`-dq37M;c503?H=#!y4mQT_vmD8Q->TE;$x zRuI~TR^Zlx*`$`Y`T%oJgJ2F$d|+qG&edDY6K%_OnWvDV~TQ$N`jknkbK&HCF|J|klMGT+xYbdM6?H~sSWZ&7; zWVhl~J49?m2o{Qrf3-<+=Sm^^M#AcO(T=MyKGZy_yQkk(x+V_M%wT2RZRDyHmcKP1 z1wSd7cbaT>I}J&>GJU#b>F&awo!~YQ?pb@eH%YfHL<#iWU%9tkV3pcl=A*(G`khRc zjzPmr_*uoinlbin(6$a~JN>&QDW+Pc{!cVhnVXs5^ok`y>mNzIRPnF`!uF+2DdY6$ z80kxLF{hQGTKFkLaRK#F9txna3f*!&KbAUeIJM@|nUGxjD0IW8Y=+OqV@UvxJbH?G z3rPnY3sQq%Uj^%W==R|w5m`(g1Na&3yNQF>DyC_fLf64jtUwJ9k-W!XH|?OKNRFqk z7Tczg7RfeSsuxxBSI0?Y165gigcuQ2Dn*$tY0=r3f_1)Ali*p^YCrm0i@DVL67p)b zS8^qu(e{MzpKc|+Jg|5&DKPmcv8AX{tM#6_CFj^wj5PD)>_(n;Sy8JaAG53*mMwbp z^lp++Zgx_f1=VY?1KPTN>|6f|IQoOu13JX6i-e`}l!a&rX1Lm(?0nA=1A&P+J0imR~Ajr0jgqj#ZmcO|@woZVd4nA)FwJtWOe zssQjqT&M3Y6N)a-XEg1%Gq9MCrDvyuRWt7zM40ves5cIZULJ^WE@ajS{q4-4_sCsV zmwGyCOeP47eDHqVNZp?oQ%Wu0uz$dVAoM9D5|q+uko z7a7?rM0TQ*eT#}g_O)zd-?Qg;^nLF8dG7mp{`mQCF4uLPb3WH~ozMBaU!Ttjgi>Cc zsK_qp&)ZXW%-mwiQ8eF!>s>{B4yO_(+MZ&19D|4z6}M&R$niwSid|_i5pd<|R7KR# z%*dh{Ouy64YYgcviN4|Lpx=!7>UYy#+wHlwwVy5RDp-76J>$p$>su1q2-D2G@XnuQ z26t)<#P@l^l*_Xi4!=I`Y-&b^4$Gh6wwbefS+-#*FV`P^M02tzh)%_)g*_j+ajxk2 z84aNwNlry+sDkj0*zg%(eqsZa6jfju$%IomyT6oo4R}b#=Q`SauDkKIQfd2G%NHl; zeY1wMIUssayuLT^;XB*%N2-cMQ`Jg$I}h!7O$xi6`P_^Tz@=V1cw&zBY-~PDPs=>e zl2FnT>kr(nApG_D>3dW9tTUK5SAdAnrx?8dz0&5oEg@esix-oN@Z9Ihd2Pfdns?CA zws7`1|HqFx`iG`WDsTBL_P_dJtP*utfwHDnGY@x={i-eV< z*hcNlDZ?EgzvNgK#w0n6zGD0$SN3vw`jb=ib$2`)E86I8!!EIFE~p=_tj(z5mU_{z z+uqZsN!peDfjLW!T3pH#mu>|xW zGv%Mxk3UX0?5KWIx@2u(KdQMw*Fw36>kdYp-iMs6`3R!Vdwf(Yb%=nYp#wfUV!6Tn z#fLP7Hl__E_Q5EcfbFsn3u9SxL~sl2{o4`JVh7dIqb>@Z^Yz2^Y>p7p9ydA3LbwP@ zYKzJ|S^7JPYl%ZxOA08Yl7DNSC<5nqX0@{YsuxovkZjeUb>aCMBU^;KS}2Xmv(s$k z{4Cg_@+)sR4hIj*W{UQ!brFb2E#g7Ripgq&)tMy9g}|@_&yv?zBzvT#KjSPLfvW&B za{;-Wl*BL=PQvtBhu<9WI(qz9UH!XukF#TEN$$q>Ng;;KZ%7?rH5&BJ;RT!97$^)j zyxVYz0R|?6L-l&J!Wb*~wbgi;Qid-X&Jp5w!+1hJw)sGT6p~we|3K-yaON8o^JMV@ z^|e#tOdl}(m*Th~Yb$|vo8pm?Ha=D*LP}I0=%$Ky_t3*L$~i%%9Sjy4YU&RfB}Yz* z$CN!lDC?VeldILm{@BSJpGpE8i*F2b5G!&vB0V}L!0qFDn55wDukG45lRb409^5z& zxtm_~(o$*yrB;Ayl9l#}r=sV4#lEMZIN5tdZL2}2+z-Et(EHh5vF>~KgoAygEe`P^t7-vPiKV|4fK{~i)LI?yo6|Ma?kae-K?O( zPN|AO=oJd^i1^9CItdnRsDlmB$gOlXtg)jYH-?u_+on-GiC4noZ?`jvw+2CP;RU31Z^z9`! zV9|fILn=t%f@RtgxGa*rPycTDuUpce94Fzw$Nblk-yIvr0z-@wItqmT)z#nsR@b6v z<_>5=_>;OT`-!NqK$WssuT%ZmyWbafP@@p|+b<{-@EZbuZ>ffZlorAQy(e>2zH)Nv zUkx7ft2banNR`R|vndiLcNVB9yoBrD=l#Bj7Q$?VuvGx|%f!Lp4tVq^kQtXx3O;s# zUhIqdYM!{Fj@|0vc`bce5V0j#&x2EMAYndPW~Ws8Jvn`kdF6`Q(KHC>=Dwxz&A{-s zAvB|KOjI{w4Pdx21e6>!I5}#lygUt}T%H|pcOY91WzWYMoS#Wp`6AQh1wR0@7-4Ix zoJO*8(9RXeX+A*3%^JD4SXe%Oc!Mc;B`4`|3uX&Sw#3MxzFR&IOhU~91q!dK0+3E- z); zOYPW-Bj%mSu!33cK=J+f)x9TaUOj)ff|2-^R91pov?MVUb&Y|{SJ@RH;Tt#DHc+xl zBnBm_f0ihZQr;qHc~jl2+3tVygk@@$AEqUBzMYV#LtUa)K~1zyrKiN{HcUn97%`aOwcecSH%`O%>f|gCe6zBnHawZMjs-r14YB#N1&KI z&*rC4C{sr(z2hT?iU9dz>AE-H7%Qa5yn4^JqV|J;%@%v~k~hB58Oy6JbwmR>|BmAv zD1X*{n(8y`I2I+-Nvz$Q<=W}dm&!w8zsbzKbUu*7b$XUDdNvxh0Cl%HT4~`m1B3zP zX3iPHz#KHCVZ(Fx3$pqZ-M*9)Od!?n(4 zMgk|&>NpvJ>=>w$_2p{A@}wJGj_ z;{YX5W|ckk11V)07H5(JH`?#tItsJ(1Pm?zg;&3fytRCqbRC8QCxIbzAZx2+v-bNA z*Z~_Aw>vffJY8djWAMZ_rXE&m_4({FAKqofsL+Ql5nCv9!)MsldcvsO(e$KVJXoc-M$C&)lha zyigrG*u9eieQzdmB^^F?HWN=Je`+@cU`o z3d3e6>MMZ8xxw(1iZ^4;_BuKF1t0bjPex%#HObws3sprG_LSt*j6l^b$-#AoCY}~m z#_bBQP5@M~0@jaa4-OvD%mJWm@?e_uArX*;=94;iiSd*(*0&bP%jP?^ci zrrtF)g|tm$-6nCYZGc&6`|6^KxZFM(c}N;xoTRZj63OAouc*%OOxHuBS%J zv4}r4uW4}Yzp8&O+n^hycrV4ApT$e-IKqX6)gEY4Y%(s|n4f)A6IAfy#j$!F)tC0? zPDOdj)Il1WBVD&bHHlxkWM;YE^qepv3pE@m`Ihg}h6YS?0^lJJr`8BSSGuzJhR9nP zw8|@;C_;ru~BD(ntj8!zcfl-qS?mcn! zs_2b_5YkW_NE0z}OBZQcS|%KMo|AOJW3|?k}vR9n=p0# zWH3MXWWFP`Mzm<3mzwyy?1j z4A-|^7eE#le1RhqDWkWZy0xw2zdSNtx-Jn!w1jh>%cVv}P>a8R&{)u)U<`prlS|nD zFmvv=4p`Q%1J2XJ|}!tjzvNq78jH&{YBF1}@^LtPc6?1eRH_4V~537S;cZ zapeC3mJJCX+6ZqE>d)K$jh?t73JQeFM6*@|DNf_{ci39YDB zTWNiEIVNX*Fh+?zTA8@HmPA>u9aAJKiCo4?xAutqsPs%N?%}iR;K0oR7gMx(0i%UZ^Hf9WRUnO4RWRbz6U;(yJ2htV#UWtrMEHDT+ zLB2@WH@AvE^|6LCLl>CPX8ao>qRpRELT_4HFG^V^%e^z_m?nnquYOo8djE^9bG&pJ zHnQ@}ZAtl65a3K#jG}So^q=*6SL2BM zLz*4&(bK?>82sjaS6E7VSW!m#m&cCYRW{CEsb5RNusSx`nevE>2Yr08I_Re}FXySy zFd3t~Lf9wAE%8A()OfhO)Zg8vqSmcds|^-9oY6(GtL;8QXqpZSKj<#G*Jq4Ry-LM-e=J1+LD(qG;%Py6nPAzXN zX>n0#$TT|MZ2Mr|DqcB8Uyk1a%<9GRWKx}x64Gm#4)Q`hqo;s7_S+${*0R=9TXSq! z19AJ?iq2F~U-l9ge()U*`6>31$0wg&9Rd06NJpwyT>qY%&fbEin@2TE%J_JfjyxPA zJ+GR8z0x2lIPCR^$A=(a{$3~MVy+9k=}~jcW}&NEM%c*q$i5Y^`oFB=UCBDjG6dQNTdauSU*;zt|n4W7pKshM$ zi_Njob8|c!E+M35U{$xtQMrT^(kIvCT8O6UA3dx>PZeL?{q@T?(I?!|S8a80HEusx z5}PRcdCu;kv~6Dx@iy2AM+$l^=VR&KP-r-Y4LGPkR}O6>nRB^35szK!90=DrR3xJ6 z^jutre5+|7(ffRCX9^ffqpmquTh=1oBuud5n?6F3J&{>X%+Ru_DjLYI5R@`FakvKf z@5Z8Z<=fOQr0B1Vw=job6NYP3yuA#%3V*q5m~p4MN~!K+pC;rj<&B3(hE1wbk3iPJ z!Ve4i`y)QENwz<2Ha)QN;C2SS&Zqd=&;~tu!z5T(JzbmOWrCWVg|Xvl-17q=ckiIE zTVFlxjh`|k-wm;-CPd8I5jkDXMO{l;zXEp>slM2mA)aYM z^Uz9Ei0-LXzJWbiVs_G?kCbuPJv8D`ns@(oP71w31S0r7ls&33!L9#}5vZ|JiOjyj zQUKOaONwwy@IZ7FT2H9IA=|{aRM}yMsSywXyk?7Ng=3&{r%1J!5W~{8^;pUNO3ht% zE-uV&wpnGyDbJK+ayPC`iIRFUWcRR?;Zlr7`bO!uBGf6CM*TC0Qd%Q;=M^p2tNn^7 z$!G2PCfsVZ$MktTZRyQc<`xX8O|A*9&MIoF_a;nzpzcdAL|r(r73seoXYLiru$mJNv~7X1*=kIV~AfSy>M^y<_jX!i!Z`#DVh&A#_f0G20d!~ zmXog4`P|j2ZaI8wdEv$9-p-I|*x;Ldx4A|v{DHcujt6CE9HOdQNm}M=GQV3|P zFA?wMv-2pu+iD)&RkYEOeUFBvwJ=B^BslRx@mRSF$#=Y?hJQRQ-lD3)`>21!2M0G+UdO4g( z{vzYWGEEMr-tD2X|67_^#@wr$3ESAJrCu7(w5E=wVRLwdj2|{iS?Xq18^iPberI$# zS)91CCfao_I&z^EXq7LRo65?h*;C-8msh69IO8^Z^x7PlT6T8okZeB&X*(XTvp>!Q zzP-CDIOTmCL5>o4j0_0FbO!+Pq(4u!ELb&@GkD-(2i_>mzt$-nRG1EqND+f~BkdFw z?7IwgX^6Qdo(C`Z;`U9yYuUB#IlQ&`wEJUtW*^pv)1YJM6|j(HPA~<9kTQT&tIJj9y{Rqy4r| zHk0rHa>R%67ZH|tkHs?y1=BxH*IZ_;V*X)vMWu9I)^l17_?^9XMqclsoi!legwp{2 zw4y!gGa zBx`v9s`R<|e(53zE@rf0y(9<}R#lw+!n|8)?T_Zu8RKD*ND_sDxeEaEY#>=mG}?Zu zXR+#i_P;`1W743rCAxLduR_2zkX^vU4gKglWYpnYRHdcM+ddN--i|fD{L1RSfi^Y> z{H8{BQ3ExBtpkfB>pBthPWB3t8|PPcvT%*!K)tRINhs1m+~%5@y0~>UdyGCq%ngl7 z<1X*D^oWn)X8-o{lfFy^k3=;5ugSp~d>#n_bY6xnilITvYD5LZJ8auE{^E{Dp)s4E z)Y#aY-->tz&gX#-qo#_QV*4ZAk&@1y-)k6@KnsW3KGb$j8<6b)J4|JgJ+jK59S=Hi z*~XEOz7Zz!zYxu5<37~IC!il{bav%uqJvICvoB!sAWvW=C#W7Qy&Mcdmd$}O!X6qT z$f!l;Zt?Umgnx@775S>NCR7mwl9_!vQqiX>sT#8f&-j+jI2m5{e>8iG*-rj0K_>L# zd+*VRv84jvud8uC7ilh1e-SMZOLoULHqkQU_W`x&Z|}@}nx^#@cIM53oOd~&FnrK0 zAo6mch_cwv^3NauGHIaP#;pm@z3=s)nPLk~2fFT1R+LVNMrFOga?HCIsl;jB9AV?G z{HKuJxeVuZ@lCSGv%8g4{+M_OSp^MZmR)mT84&Hg<+SW1v1M0Bv-?```jD?+FU(cW z>!oy#RN~j5{}Q>TY!f|OCnG%Q8dVGUJI;NAl-G)+I7^EtXYEI?uYgQ#!m7iXfWVZB zbYVy&o5in|D5>y~1wXU#dv3Il;90%J;GwagGVc}s_n+5AZp=JN?mJ03Jv!Q%cz7K* z$;7KnX&Y2Z8$q)Sa_LlE#DTnggW@O0$%vh<$QIrQ8 z3-E~`h#N3H8Ps0VWVN=Fd=tcJr2}vBCdpY2Zzo=5 zMHs^_EOf(zAZ>e(T_hQB;i8H^1O~RR2+-``I^j@>n-DtvG`A3ioL1; zoXV95utjW%7Fd}lr|Yj44wj5-XI3+1+Nw8XRy#(`GY9AVz9l}y+Y|kioSEXxrzM%U=K`#Mao61l5ihT0+Z`{c*0WxfKm?b5+E9xcPdy|2rKjR z{{B|CsWyT(Z9g-{GctDtrryoiF>#U!%*jdorc`dNU4Mz>@s4LxsYMZ|ieCwiEGWF} zT?X3;(xuk=Jxau5^zFX^q8RPp$74=An?3#y2&Byq{f&njMZ>bd)u2CrA|iDvL0I6X z2bEpD7}Hk#3SQGVsW48ahijutNZ}&agn&>tHUOZ1fP)p=Lk-Yvp{vp|9&Et+Pw{M z$A5Ga%U}Oe`!xA~n(U+{QFt8WKXBgft0rSe9sjGF8rVw_x{!aighKrHIO}=-he1L} zVo$|G|Jf3FFaB$sSmpm=5DHS>GlCrdv?#!P|20k$-~V9{YBCi)ZRvkn6udA0J+mQbUJ<;hy literal 0 HcmV?d00001 diff --git a/go.work.sum b/go.work.sum index e69dce23b6..29e4435f66 100644 --- a/go.work.sum +++ b/go.work.sum @@ -249,8 +249,6 @@ cloud.google.com/go/websecurityscanner v1.6.1 h1:CfEF/vZ+xXyAR3zC9iaC/QRdf1MEgS2 cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= cloud.google.com/go/workflows v1.11.1 h1:2akeQ/PgtRhrNuD/n1WvJd5zb7YyuDZrlOanBj2ihPg= cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3 h1:hJiie5Bf3QucGRa4ymsAUOxyhYwGEz1xrsVk0P8erlw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0 h1:SPOUaucgtVls75mg+X7CXigS71EnsfVUK/2CgVrwqgw= @@ -301,7 +299,6 @@ github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= @@ -329,7 +326,6 @@ github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -343,6 +339,7 @@ github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloD github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= @@ -520,8 +517,6 @@ github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA= @@ -547,7 +542,6 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k= github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw= -github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= @@ -701,8 +695,6 @@ github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= -github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= @@ -748,6 +740,7 @@ github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= +github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= @@ -898,8 +891,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537 h1:YGaxtkYjb8mnTvtufv2LKLwCQu2/C7qFB7UtrOlTWOY= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133 h1:JtcyT0rk/9PKOdnKQzuDR+FSjh7SGtJwpgVpfZBRKlQ= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= -github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= @@ -951,8 +942,6 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9 github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= -github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA= github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= @@ -1000,7 +989,6 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEa go4.org v0.0.0-20180809161055-417644f6feb5 h1:+hE86LblG4AyDgwMCLTE6FOlM9+qjHSYS+rKqxUVdsM= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d h1:E2M5QgjZ/Jg+ObCQAudsXxuTsLj7Nl5RV/lZcQZmKSo= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= @@ -1020,7 +1008,6 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 h1:xYq6+9AtI+xP3M4r0N1hCkHrInHDBohhquRgx9Kk6gI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From c2d4d428e7b2d174cee6161370177eca26a13979 Mon Sep 17 00:00:00 2001 From: Tedi Mitiku Date: Tue, 20 Feb 2024 16:08:27 -0500 Subject: [PATCH 084/102] fix: admonition in github guide (#2195) --- docs/docs/guides/running-private-packages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/guides/running-private-packages.md b/docs/docs/guides/running-private-packages.md index 0165cd86a5..9a05d06430 100644 --- a/docs/docs/guides/running-private-packages.md +++ b/docs/docs/guides/running-private-packages.md @@ -7,7 +7,7 @@ sidebar_position: 14 Kurtosis CLI supports the ability to run private packages hosted on GitHub via `kurtosis github login`. This guide assumes that you have [Kurtosis installed](../get-started/installing-the-cli.md) and a package hosted GitHub that is private. -:::warn +:::note GitHub Login is not yet supported over Kubernetes backend. Please create an [issue](https://github.com/kurtosis-tech/kurtosis/issues) to request this feature! ::: From 9b5e0279d254391a6f580de732929366182dd4b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 22:07:01 -0500 Subject: [PATCH 085/102] build(deps): Bump ip from 2.0.0 to 2.0.1 in /enclave-manager/web (#2197) Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ip&package-manager=npm_and_yarn&previous-version=2.0.0&new-version=2.0.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/kurtosis-tech/kurtosis/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- enclave-manager/web/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/enclave-manager/web/yarn.lock b/enclave-manager/web/yarn.lock index a75ef5da2b..0715d8f0c1 100644 --- a/enclave-manager/web/yarn.lock +++ b/enclave-manager/web/yarn.lock @@ -9168,9 +9168,9 @@ invariant@^2.2.4: loose-envify "^1.0.0" ip@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" - integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" + integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== ipaddr.js@1.9.1: version "1.9.1" From bdaa1ba392a265f183614a47d068b120e114bdb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 03:32:07 +0000 Subject: [PATCH 086/102] build(deps-dev): Bump axios from 0.24.0 to 0.28.0 in /internal_testsuites/typescript (#2198) Bumps [axios](https://github.com/axios/axios) from 0.24.0 to 0.28.0.
Release notes

Sourced from axios's releases.

Release v0.28.0

Release notes:

Bug Fixes

  • fix(security): fixed CVE-2023-45857 by backporting withXSRFToken option to v0.x (#6091)

Backports from v1.x:

  • Allow null indexes on formSerializer and paramsSerializer v0.x (#4961)
  • Fixing content-type header repeated #4745
  • Fixed timeout error message for HTTP 4738
  • Added axios.formToJSON method (#4735)
  • URL params serializer (#4734)
  • Fixed toFormData Blob issue on node>v17 #4728
  • Adding types for progress event callbacks #4675
  • Fixed max body length defaults #4731
  • Added data URL support for node.js (#4725)
  • Added isCancel type assert (#4293)
  • Added the ability for the url-encoded-form serializer to respect the formSerializer config (#4721)
  • Add string[] to AxiosRequestHeaders type (#4322)
  • Allow type definition for axios instance methods (#4224)
  • Fixed AxiosError stack capturing; (#4718)
  • Fixed AxiosError status code type; (#4717)
  • Adding Canceler parameters config and request (#4711)
  • fix(types): allow to specify partial default headers for instance creation (#4185)
  • Added blob to the list of protocols supported by the browser (#4678)
  • Fixing Z_BUF_ERROR when no content (#4701)
  • Fixed race condition on immediate requests cancellation (#4261)
  • Added a clear() function to the request and response interceptors object so a user can ensure that all interceptors have been removed from an Axios instance axios/axios#4248
  • Added generic AxiosAbortSignal TS interface to avoid importing AbortController polyfill (#4229)
  • Fix TS definition for AxiosRequestTransformer (#4201)
  • Use type alias instead of interface for AxiosPromise (#4505)
  • Include request and config when creating a CanceledError instance (#4659)
  • Added generic TS types for the exposed toFormData helper (#4668)
  • Optimized the code that checks cancellation (#4587)
  • Replaced webpack with rollup (#4596)
  • Added stack trace to AxiosError (#4624)
  • Updated AxiosError.config to be optional in the type definition (#4665)
  • Removed incorrect argument for NetworkError constructor (#4656)

v0.27.2

Fixes and Functionality:

  • Fixed FormData posting in browser environment by reverting #3785 (#4640)
  • Enhanced protocol parsing implementation (#4639)
  • Fixed bundle size

v0.27.1

Fixes and Functionality:

  • Removed import of url module in browser build due to huge size overhead and builds being broken (#4594)
  • Bumped follow-redirects to ^1.14.9 (#4615)

... (truncated)

Changelog

Sourced from axios's changelog.

0.28.0 (2024-02-12)

Release notes:

Bug Fixes

  • fix(security): fixed CVE-2023-45857 by backporting withXSRFToken option to v0.x (#6091)

Backports from v1.x:

  • Allow null indexes on formSerializer and paramsSerializer v0.x (#4961)
  • Fixing content-type header repeated #4745
  • Fixed timeout error message for HTTP 4738
  • Added axios.formToJSON method (#4735)
  • URL params serializer (#4734)
  • Fixed toFormData Blob issue on node>v17 #4728
  • Adding types for progress event callbacks #4675
  • Fixed max body length defaults #4731
  • Added data URL support for node.js (#4725)
  • Added isCancel type assert (#4293)
  • Added the ability for the url-encoded-form serializer to respect the formSerializer config (#4721)
  • Add string[] to AxiosRequestHeaders type (#4322)
  • Allow type definition for axios instance methods (#4224)
  • Fixed AxiosError stack capturing; (#4718)
  • Fixed AxiosError status code type; (#4717)
  • Adding Canceler parameters config and request (#4711)
  • fix(types): allow to specify partial default headers for instance creation (#4185)
  • Added blob to the list of protocols supported by the browser (#4678)
  • Fixing Z_BUF_ERROR when no content (#4701)
  • Fixed race condition on immediate requests cancellation (#4261)
  • Added a clear() function to the request and response interceptors object so a user can ensure that all interceptors have been removed from an Axios instance axios/axios#4248
  • Added generic AxiosAbortSignal TS interface to avoid importing AbortController polyfill (#4229)
  • Fix TS definition for AxiosRequestTransformer (#4201)
  • Use type alias instead of interface for AxiosPromise (#4505)
  • Include request and config when creating a CanceledError instance (#4659)
  • Added generic TS types for the exposed toFormData helper (#4668)
  • Optimized the code that checks cancellation (#4587)
  • Replaced webpack with rollup (#4596)
  • Added stack trace to AxiosError (#4624)
  • Updated AxiosError.config to be optional in the type definition (#4665)
  • Removed incorrect argument for NetworkError constructor (#4656)

0.27.2 (April 27, 2022)

Fixes and Functionality:

  • Fixed FormData posting in browser environment by reverting #3785 (#4640)
  • Enhanced protocol parsing implementation (#4639)
  • Fixed bundle size

0.27.1 (April 26, 2022)

... (truncated)

Commits
  • 3b7635a [Release] v0.28.0 (#6211)
  • 27c0076 feat(backport): added ability for paramsSerializer to handle function; (#6227)
  • 80c3d74 chore(ci): backported publish action; (#6224)
  • 2755df5 fix(security): fixed CVE-2023-45857 by backporting withXSRFToken option to ...
  • 880b42e docs: Fix a typo in README
  • c4bf0a4 Allow null indexes on formSerializer and paramsSerializer v0.x (#4961)
  • 1e2679f fix: [Types] Type of header in AxiosRequestConfig / for Axios.create is incor...
  • 80b546c fix: loosing request header (#4858) (#4871)
  • 6acb5ef feat: brower platform add data protocol. (#4814)
  • bbb2264 fix(typing): axios response headers can be undefined (#4813)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=axios&package-manager=npm_and_yarn&previous-version=0.24.0&new-version=0.28.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/kurtosis-tech/kurtosis/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- internal_testsuites/typescript/package.json | 2 +- internal_testsuites/typescript/yarn.lock | 30 ++++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/internal_testsuites/typescript/package.json b/internal_testsuites/typescript/package.json index 051fc3a0e2..a0c1757b82 100644 --- a/internal_testsuites/typescript/package.json +++ b/internal_testsuites/typescript/package.json @@ -7,7 +7,7 @@ "@types/jest": "^29.0.0", "@types/node": "^16.11.11", "@types/utf8": "^3.0.0", - "axios": "^0.24.0", + "axios": "^0.28.0", "example-api-server-api-lib": "^0.3.4", "example-datastore-server-api-lib": "^0.4.4", "jest": "^27.3.1", diff --git a/internal_testsuites/typescript/yarn.lock b/internal_testsuites/typescript/yarn.lock index b9faf1d50b..d13e570af4 100644 --- a/internal_testsuites/typescript/yarn.lock +++ b/internal_testsuites/typescript/yarn.lock @@ -893,12 +893,14 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@^0.24.0: - version "0.24.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" - integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== +axios@^0.28.0: + version "0.28.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.28.0.tgz#801a4d991d0404961bccef46800e1170f8278c89" + integrity sha512-Tu7NYoGY4Yoc7I+Npf9HhUMtEEpV7ZiLH9yndTCoNhcpBH0kwcvFbzYN9/u5QKI5A6uefjsNNWaz5olJVYS62Q== dependencies: - follow-redirects "^1.14.4" + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" babel-jest@^27.5.1: version "27.5.1" @@ -1386,7 +1388,7 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -follow-redirects@^1.14.4: +follow-redirects@^1.15.0: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== @@ -1400,6 +1402,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -2190,7 +2201,7 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== kurtosis-sdk@../../api/typescript: - version "0.86.12" + version "0.87.0" dependencies: "@bufbuild/connect" "^0.12.0" "@bufbuild/connect-web" "^0.12.0" @@ -2528,6 +2539,11 @@ protobufjs@^7.2.4: "@types/node" ">=13.7.0" long "^5.0.0" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" From ad00ee13c1cab56cd6fb7455dd62900f7fa6b0c0 Mon Sep 17 00:00:00 2001 From: Ben Gazzard Date: Wed, 21 Feb 2024 14:50:01 +0000 Subject: [PATCH 087/102] feat: use description field in emui logs (#2199) ## Description: This PR uses the new `description` field in the enclave logs viewer. By default this field is now used to render starlark instructions, rather than the raw field (from #2147 ). ### Demo https://github.com/kurtosis-tech/kurtosis/assets/4419574/a1aa4754-f68b-4ea5-8af8-068531db4dab ## Is this change user facing? Yes ## References (if applicable): * briefed on slack. --- .../components/widgets/PortsSummary.tsx | 6 +- .../enclaves/enclave/logs/EnclaveLogs.tsx | 81 ++++++++++++++++--- .../components/src/KurtosisThemeProvider.tsx | 1 - 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/PortsSummary.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/PortsSummary.tsx index 6df7e7f82a..ed21b36c82 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/PortsSummary.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/PortsSummary.tsx @@ -1,7 +1,7 @@ import { Card, - Flex, Popover, + PopoverBody, PopoverContent, PopoverTrigger, Table, @@ -29,11 +29,11 @@ export const PortsSummary = ({ privatePorts, publicPorts }: PortsSummaryProps) = - + - + ); diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/enclave/logs/EnclaveLogs.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/enclave/logs/EnclaveLogs.tsx index 7d14acf3d1..23808cb827 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/enclave/logs/EnclaveLogs.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/enclave/logs/EnclaveLogs.tsx @@ -1,8 +1,25 @@ -import { ButtonGroup, CircularProgress, Flex, Icon, Tag } from "@chakra-ui/react"; +import { + ButtonGroup, + CircularProgress, + Flex, + FormControl, + FormLabel, + Icon, + IconButton, + Popover, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverHeader, + PopoverTrigger, + Switch, + Tag, +} from "@chakra-ui/react"; import { StarlarkRunResponseLine } from "enclave-manager-sdk/build/api_container_service_pb"; import { AppPageLayout, isAsyncIterable, LogLineMessage, LogViewer, stringifyError } from "kurtosis-ui-components"; -import { useEffect, useState } from "react"; -import { FiCheck, FiX } from "react-icons/fi"; +import { useEffect, useMemo, useState } from "react"; +import { FiCheck, FiSettings, FiX } from "react-icons/fi"; import { Location, useBlocker, useLocation, useNavigate } from "react-router-dom"; import { EditEnclaveButton } from "../../components/EditEnclaveButton"; import { LogNavigationWarningModal } from "../../components/modals/LogNavigationWarningModal"; @@ -20,10 +37,17 @@ type EnclaveLogStage = const LOG_STARTING_EXECUTION = "Starting execution"; -export function starlarkResponseLineToLogLineMessage(l: StarlarkRunResponseLine): LogLineMessage { +export function starlarkResponseLineToLogLineMessage( + l: StarlarkRunResponseLine, + shouldUseDescriptionField: boolean, +): LogLineMessage { switch (l.runResponseLine.case) { case "instruction": - return { message: l.runResponseLine.value.executableInstruction }; + return { + message: shouldUseDescriptionField + ? l.runResponseLine.value.description + : l.runResponseLine.value.executableInstruction, + }; case "progressInfo": return { message: l.runResponseLine.value.currentStepInfo[l.runResponseLine.value.currentStepNumber] }; case "instructionResult": @@ -46,7 +70,17 @@ export const EnclaveLogs = () => { const navigator = useNavigate(); const location = useLocation() as Location<{ logs: AsyncIterable }>; const [progress, setProgress] = useState({ stage: "waiting" }); - const [logLines, setLogLines] = useState([]); + const [shouldUseDescriptionField, setShouldUseDescriptionField] = useState(true); + const [rawLogLines, setRawLogLines] = useState<(StarlarkRunResponseLine | { message: string; status: "error" })[]>( + [], + ); + const logLines = useMemo((): LogLineMessage[] => { + return rawLogLines.map((rawLogLine) => + rawLogLine.hasOwnProperty("status") + ? (rawLogLine as LogLineMessage) + : starlarkResponseLineToLogLineMessage(rawLogLine as StarlarkRunResponseLine, shouldUseDescriptionField), + ); + }, [rawLogLines, shouldUseDescriptionField]); const blocker = useBlocker(({ currentLocation, nextLocation }) => currentLocation.pathname !== nextLocation.pathname); @@ -54,15 +88,15 @@ export const EnclaveLogs = () => { let cancelled = false; (async () => { if (location.state && isAsyncIterable(location.state.logs)) { - setLogLines([]); + setRawLogLines([]); setProgress({ stage: "waiting" }); try { for await (const line of location.state.logs) { if (cancelled) { return; } - const parsedLine = starlarkResponseLineToLogLineMessage(line); - setLogLines((logLines) => [...logLines, parsedLine]); + const parsedLine = starlarkResponseLineToLogLineMessage(line, shouldUseDescriptionField); + setRawLogLines((logLines) => [...logLines, line]); setProgress((oldProgress) => { if (line.runResponseLine.case === "progressInfo") { if (oldProgress.stage === "waiting") { @@ -97,7 +131,7 @@ export const EnclaveLogs = () => { if (cancelled) { return; } - setLogLines((logLines) => [...logLines, { message: `Error: ${stringifyError(error)}`, status: "error" }]); + setRawLogLines((logLines) => [...logLines, { message: `Error: ${stringifyError(error)}`, status: "error" }]); await Promise.all([refreshStarlarkRun(enclave), refreshServices(enclave), refreshFilesAndArtifacts(enclave)]); } finally { updateStarlarkFinishedInEnclave(enclave); @@ -123,6 +157,10 @@ export const EnclaveLogs = () => { ? 100 : 0; + const handleToggleDescriptive = (e: React.ChangeEvent) => { + setShouldUseDescriptionField(e.target.checked); + }; + return ( <> @@ -134,6 +172,29 @@ export const EnclaveLogs = () => { + + + } aria-label={"Settings"} /> + + + + + Settings + + + + Use descriptive starlark output + + + + + + Date: Wed, 21 Feb 2024 16:46:22 -0500 Subject: [PATCH 088/102] fix: Fix the connect to enclave CLI commands in the EM UI (#2203) Add api key environment variable set command to the list of connect to enclave CLI commands in the EM UI. ## Is this change user facing? YES ## References (if applicable): https://github.com/kurtosis-tech/kurtosis-cloud-frontend/pull/84 --- .../emui/enclaves/components/modals/ConnectEnclaveModal.tsx | 4 +++- .../emui/enclaves/components/widgets/ConnectEnclaveButton.tsx | 4 +++- .../web/packages/app/src/emui/enclaves/enclave/Enclave.tsx | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx index 6e1c3b8264..286921e20c 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/modals/ConnectEnclaveModal.tsx @@ -13,12 +13,14 @@ import { EnclaveFullInfo } from "../../types"; export type ConnectEnclaveModalProps = { enclave: EnclaveFullInfo; instanceUUID: string; + apiKey: string; isOpen: boolean; onClose: () => void; }; -export const ConnectEnclaveModal = ({ isOpen, onClose, enclave, instanceUUID }: ConnectEnclaveModalProps) => { +export const ConnectEnclaveModal = ({ isOpen, onClose, enclave, instanceUUID, apiKey }: ConnectEnclaveModalProps) => { const commands = ` + export KURTOSIS_CLOUD_API_KEY="${apiKey}" kurtosis cloud load ${instanceUUID} kurtosis enclave connect ${enclave.name} kurtosis enclave inspect ${enclave.name}`; diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx index 295c025d08..322f63b742 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/components/widgets/ConnectEnclaveButton.tsx @@ -7,9 +7,10 @@ import { ConnectEnclaveModal } from "../modals/ConnectEnclaveModal"; type ConnectEnclaveButtonProps = ButtonProps & { enclave: EnclaveFullInfo; instanceUUID: string; + apiKey: string; }; -export const ConnectEnclaveButton = ({ enclave, instanceUUID, ...buttonProps }: ConnectEnclaveButtonProps) => { +export const ConnectEnclaveButton = ({ enclave, instanceUUID, apiKey, ...buttonProps }: ConnectEnclaveButtonProps) => { const [showModal, setShowModal] = useState(false); return ( @@ -29,6 +30,7 @@ export const ConnectEnclaveButton = ({ enclave, instanceUUID, ...buttonProps }: setShowModal(false)} /> diff --git a/enclave-manager/web/packages/app/src/emui/enclaves/enclave/Enclave.tsx b/enclave-manager/web/packages/app/src/emui/enclaves/enclave/Enclave.tsx index c3887e6e51..aa79049c07 100644 --- a/enclave-manager/web/packages/app/src/emui/enclaves/enclave/Enclave.tsx +++ b/enclave-manager/web/packages/app/src/emui/enclaves/enclave/Enclave.tsx @@ -46,6 +46,7 @@ const EnclaveImpl = ({ enclave }: EnclaveImplProps) => { }; const instanceUUID = Cookies.get("_kurtosis_instance_id") || ""; + const apiKey = Cookies.get("_kurtosis_api_key") || ""; return ( @@ -58,7 +59,7 @@ const EnclaveImpl = ({ enclave }: EnclaveImplProps) => { - + From 90c6f9f685c4ee7cf9eb8cb91f71ac2419fe5f0c Mon Sep 17 00:00:00 2001 From: kurtosisbot <89932784+kurtosisbot@users.noreply.github.com> Date: Wed, 21 Feb 2024 17:10:46 -0500 Subject: [PATCH 089/102] chore(main): release 0.87.1 (#2196) :robot: I have created a release *beep* *boop* --- ## [0.87.1](https://github.com/kurtosis-tech/kurtosis/compare/0.87.0...0.87.1) (2024-02-21) ### Features * use description field in emui logs ([#2199](https://github.com/kurtosis-tech/kurtosis/issues/2199)) ([ad00ee1](https://github.com/kurtosis-tech/kurtosis/commit/ad00ee13c1cab56cd6fb7455dd62900f7fa6b0c0)) ### Bug Fixes * admonition in github guide ([#2195](https://github.com/kurtosis-tech/kurtosis/issues/2195)) ([c2d4d42](https://github.com/kurtosis-tech/kurtosis/commit/c2d4d428e7b2d174cee6161370177eca26a13979)) * Fix the connect to enclave CLI commands in the EM UI ([#2203](https://github.com/kurtosis-tech/kurtosis/issues/2203)) ([2218f4b](https://github.com/kurtosis-tech/kurtosis/commit/2218f4bb561d5b3e15977f3c28d8c3c7a1811699)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: kurtosisbot --- CHANGELOG.md | 13 +++++++++++++ LICENSE.md | 4 ++-- api/golang/kurtosis_version/kurtosis_version.go | 2 +- api/rust/Cargo.toml | 2 +- api/typescript/package.json | 2 +- .../src/kurtosis_version/kurtosis_version.ts | 2 +- enclave-manager/web/lerna.json | 2 +- enclave-manager/web/packages/app/package.json | 4 ++-- .../web/packages/components/package.json | 2 +- version.txt | 2 +- 10 files changed, 24 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afc2d08bcf..b02f85bc9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [0.87.1](https://github.com/kurtosis-tech/kurtosis/compare/0.87.0...0.87.1) (2024-02-21) + + +### Features + +* use description field in emui logs ([#2199](https://github.com/kurtosis-tech/kurtosis/issues/2199)) ([ad00ee1](https://github.com/kurtosis-tech/kurtosis/commit/ad00ee13c1cab56cd6fb7455dd62900f7fa6b0c0)) + + +### Bug Fixes + +* admonition in github guide ([#2195](https://github.com/kurtosis-tech/kurtosis/issues/2195)) ([c2d4d42](https://github.com/kurtosis-tech/kurtosis/commit/c2d4d428e7b2d174cee6161370177eca26a13979)) +* Fix the connect to enclave CLI commands in the EM UI ([#2203](https://github.com/kurtosis-tech/kurtosis/issues/2203)) ([2218f4b](https://github.com/kurtosis-tech/kurtosis/commit/2218f4bb561d5b3e15977f3c28d8c3c7a1811699)) + ## [0.87.0](https://github.com/kurtosis-tech/kurtosis/compare/0.86.25...0.87.0) (2024-02-20) diff --git a/LICENSE.md b/LICENSE.md index 749a81e948..f16e177355 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.87.0 +Licensed Work: Kurtosis 0.87.1 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2028-02-20 +Change Date: 2028-02-21 Change License: Apache 2.0 (Apache License, Version 2.0) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 49ba14ae23..9d55f0d435 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.87.0" + KurtosisVersion = "0.87.1" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 8787dd9520..421fd79b43 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.87.0" +version = "0.87.1" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index c15e20b076..1397cc0995 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.87.0", + "version": "0.87.1", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index 906fc996f8..7edcf3c642 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.87.0" +export const KURTOSIS_VERSION: string = "0.87.1" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json index 8f0a2d6d87..82f6249227 100644 --- a/enclave-manager/web/lerna.json +++ b/enclave-manager/web/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.87.0", + "version": "0.87.1", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": false, diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json index f4a41b9666..de11f18a9b 100644 --- a/enclave-manager/web/packages/app/package.json +++ b/enclave-manager/web/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@kurtosis/emui-app", - "version": "0.87.0", + "version": "0.87.1", "private": true, "homepage": ".", "dependencies": { @@ -10,7 +10,7 @@ "html-react-parser": "^4.2.2", "js-cookie": "^3.0.5", "kurtosis-cloud-indexer-sdk": "^0.0.2", - "kurtosis-ui-components": "0.87.0", + "kurtosis-ui-components": "0.87.1", "react-error-boundary": "^4.0.11", "react-hook-form": "^7.47.0", "react-mentions": "^4.4.10", diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json index 8b38eb1bb3..7adf4174d3 100644 --- a/enclave-manager/web/packages/components/package.json +++ b/enclave-manager/web/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "kurtosis-ui-components", - "version": "0.87.0", + "version": "0.87.1", "private": false, "main": "build/index", "description": "This repo contains components used by Kurtosis UI applications.", diff --git a/version.txt b/version.txt index 359ee08a7c..7921aa127f 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.87.0 +0.87.1 From 0c71e5ce4ddf660f6ea221b08241eeac0378db00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:27:14 +0100 Subject: [PATCH 090/102] docs: fix "write your first package" curl commands (#2165) ## Description: - Fix some of the curl commands in the docs by adding a space between `curl` and the verb (e.g. `curl -XGET` becomes `curl -X GET`). - A bunch of automatic linter updates (that I can remove if needed). ## Is this change user facing? YES ## References (if applicable): --------- Co-authored-by: Gyanendra Mishra --- .../get-started/write-your-first-package.md | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/docs/docs/get-started/write-your-first-package.md b/docs/docs/get-started/write-your-first-package.md index 821fe0af3f..1ff7b1cae2 100644 --- a/docs/docs/get-started/write-your-first-package.md +++ b/docs/docs/get-started/write-your-first-package.md @@ -12,7 +12,7 @@ Introduction This guide takes ~15 minutes and will walk you through writing your first Kurtosis package. You'll set up a Postgres database and an API server which connects to the database. -:::tip What You'll Do +:::tip What You'll Do - Start a containerized Postgres database - Seed your database with test data - Connect an API server to your database @@ -23,11 +23,11 @@ This guide takes ~15 minutes and will walk you through writing your first Kurtos
TL;DR Version This quickstart is in a "code along" format. You can also dive straight into running the end results and exploring the code too. - + **Open the Playground: [Start](https://gitpod.io/?autostart=true&editor=code#https://github.com/kurtosis-tech/quickstart-gitpod)** Click on the "New Workspace" button! You don't have to worry about the Context URL, Editor or Class. It's all pre-configured for you. - +
Setup @@ -200,23 +200,23 @@ We call this approach [multi-phase runs][multi-phase-runs-reference]. While this Add some data ------------- -A database without data is a fancy heater, so let's add some. +A database without data is a fancy heater, so let's add some. Your two options for seeding a Postgres database are: 1. Making a sequence of PSQL commands via the `psql` binary 2. Using `pg_restore` to load a package of data -Both are possible in Kurtosis, but for this tutorial we'll use `pg_restore` to seed your database with a TAR of DVD rental information, [courtesy of postgresqltutorial.com](https://www.postgresqltutorial.com/postgresql-getting-started/postgresql-sample-database/). +Both are possible in Kurtosis, but for this tutorial we'll use `pg_restore` to seed your database with a TAR of DVD rental information, [courtesy of postgresqltutorial.com](https://www.postgresqltutorial.com/postgresql-getting-started/postgresql-sample-database/). #### Without Kurtosis -Normally going this route (using `pg_restore`) requires downloading the seed data to your local machine, starting Postgres, writing a pile of Bash to copy the seed data to the Postgres server, and then finally running the `pg_restore` command. If you forget to check if the database is available, you may get flakes when you try to use the seeding logic in a test. +Normally going this route (using `pg_restore`) requires downloading the seed data to your local machine, starting Postgres, writing a pile of Bash to copy the seed data to the Postgres server, and then finally running the `pg_restore` command. If you forget to check if the database is available, you may get flakes when you try to use the seeding logic in a test. Alternatively, you could use Docker Compose to volume-mount the data TAR into the Postgres server, but you'd still need to handle Postgres availability and sequencing the `pg_restore` afterwards. #### With Kurtosis -By contrast, Kurtosis Starlark scripts can use data as a first-class primitive and sequence tasks such as `pg_restore` into the plan. +By contrast, Kurtosis Starlark scripts can use data as a first-class primitive and sequence tasks such as `pg_restore` into the plan. Let's see it in action, and we'll explain what's happening afterwards. @@ -395,7 +395,7 @@ def run(plan, args): return result ``` -A [files artifact][files-artifacts-reference] is Kurtosis' first-class data primitive and is a TGZ of arbitrary files living inside an enclave. So long as a files artifact exists, Kurtosis knows how to mount its contents on a service. +A [files artifact][files-artifacts-reference] is Kurtosis' first-class data primitive and is a TGZ of arbitrary files living inside an enclave. So long as a files artifact exists, Kurtosis knows how to mount its contents on a service. #### You mounted and seeded the data into your Postgres instance Next, you mounted the seed data, stored in your enclave now as a files artifact, into your Postgres instance using the `ServiceConfig.files` option: @@ -512,7 +512,7 @@ We got a failure, just like we might when building a real system! ```text > add_service name="api" config=ServiceConfig(image="postgrest/postgrest:v10.2.0.20230209", ports={"http": PortSpec(number=3000, application_protocol="http")}, env_vars={"PGRST_DB_ANON_ROLE": "app_user", "PGRST_DB_URI": "postgresql://postgres:password@{{kurtosis:4d65eca66b5749df8988419ae31dda21:ip_address.runtime_value}}:5432/app_db"}) -There was an error executing Starlark code +There was an error executing Starlark code An error occurred executing instruction (number 4) at DEFAULT_PACKAGE_ID_FOR_SCRIPT[54:27]: add_service(name="api", config=ServiceConfig(image="postgrest/postgrest:v10.2.0.20230209", ports={"http": PortSpec(number=3000, application_protocol="http")}, env_vars={"PGRST_DB_ANON_ROLE": "app_user", "PGRST_DB_URI": "postgresql://postgres:password@{{kurtosis:4d65eca66b5749df8988419ae31dda21:ip_address.runtime_value}}:5432/app_db"})) Caused by: Unexpected error occurred starting service 'api' @@ -521,9 +521,7 @@ An error occurred executing instruction (number 4) at DEFAULT_PACKAGE_ID_FOR_SCR 09/May/2023:19:18:41 +0000: Attempting to connect to the database... 09/May/2023:19:18:41 +0000: {"code":"PGRST000","details":"connection to server at \"10.1.0.3\", port 5432 failed: FATAL: password authentication failed for user \"postgres\"\n","hint":null,"message":"Database connection error. Retrying the connection."} 09/May/2023:19:18:41 +0000: connection to server at "10.1.0.3", port 5432 failed: FATAL: password authentication failed for user "postgres" - postgrest: thread killed - == FINISHED SERVICE 'api' LOGS =================================== Caused by: An error occurred while waiting for all TCP and UDP ports to be open Caused by: Unsuccessful ports check for IP '10.1.0.4' and port spec '{number:3000 transportProtocol:0 applicationProtocol:0xc006662e10 wait:0xc00662d510}', even after '2' retries with '500' milliseconds in between retries. Timeout '15s' has been reached @@ -557,7 +555,7 @@ UUID Name Ports 45b355fc810b postgres postgres: 5432/tcp -> postgresql://127.0.0.1:59821 RUNNING ``` -From the above, we can see that the PostgREST service (named: `api`) is not in the 'User Services' list, so we can infer that it crashed when it was starting. +From the above, we can see that the PostgREST service (named: `api`) is not in the 'User Services' list, so we can infer that it crashed when it was starting. You can also grab the PostgREST logs... @@ -636,7 +634,7 @@ ce90b471a982 postgres postgres: 5432/tcp -> postgresql://127.0.0.1:59883
Review: Add an API -In this section, you spun up a new PostgREST service (that we named `api` for readability) with a dependency on the Postgres service. Normally, PostgREST needs to know the IP address or hostname of the Postgres service, and we said earlier that Starlark (the Interpretation phase) can never know Execution values. +In this section, you spun up a new PostgREST service (that we named `api` for readability) with a dependency on the Postgres service. Normally, PostgREST needs to know the IP address or hostname of the Postgres service, and we said earlier that Starlark (the Interpretation phase) can never know Execution values. So how did the services get connected? @@ -703,13 +701,13 @@ You can paste the URL from your output into your browser (or Cmd+click if you're Now make a request to insert a row into the database (replacing `$YOUR_PORT` with the `http` port from your `enclave inspect` output for the PostgREST service that we named `api`)... ```bash -curl -XPOST -H "content-type: application/json" http://127.0.0.1:$YOUR_PORT/actor --data '{"first_name": "Kevin", "last_name": "Bacon"}' +curl -X POST -H "content-type: application/json" http://127.0.0.1:$YOUR_PORT/actor --data '{"first_name": "Kevin", "last_name": "Bacon"}' ``` ...and then query for it (again replacing `$YOUR_PORT` with your port)... ```bash -curl -XGET "http://127.0.0.1:$YOUR_PORT/actor?first_name=eq.Kevin&last_name=eq.Bacon" +curl -X GET "http://127.0.0.1:$YOUR_PORT/actor?first_name=eq.Kevin&last_name=eq.Bacon" ``` ...to get it back: @@ -867,7 +865,7 @@ kurtosis run github.com/YOUR_GITHUB_USERNAME/REPOSITORY_NAME '{"actors": [{"firs where `YOUR_GITHUB_USERNAME` is your Github username, and `REPOSITORY_NAME` is the name of your Github repository that houses your `kurtosis.yml` and `main.star` files. - [future-references-reference]: ../../advanced-concepts/future-references.md diff --git a/internal_testsuites/golang/testsuite/startosis_add_service_test/startosis_add_service_port_url_test.go b/internal_testsuites/golang/testsuite/startosis_add_service_test/startosis_add_service_port_url_test.go new file mode 100644 index 0000000000..76ef02f0f7 --- /dev/null +++ b/internal_testsuites/golang/testsuite/startosis_add_service_test/startosis_add_service_port_url_test.go @@ -0,0 +1,83 @@ +package startosis_add_service_test + +import ( + "context" + "github.com/kurtosis-tech/kurtosis/api/golang/core/lib/services" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + "k8s.io/utils/strings/slices" +) + +const ( + addServicePortTest = ` +CONTAINER_IMAGE = "kurtosistech/example-datastore-server" +SERVICE_NAME = "` + serviceName + `" +SERVICE_NAME_2 = "` + serviceName2 + `" +GRPC_PORT = 1323 +SUCCESS_CODE = 0 + +def run(plan): + config = ServiceConfig( + image = CONTAINER_IMAGE, + max_cpu=500, + min_cpu=100, + memory_allocation=256, + max_memory=1024, + min_memory=512, + ports = { + "grpc": PortSpec(number = GRPC_PORT, transport_protocol = "TCP", application_protocol="grpc") + } + ) + datastore_1 = plan.add_service(name = SERVICE_NAME, config = config) + plan.verify(datastore_1.ports["grpc"].url, "==", "grpc://` + serviceName + `:" + str(GRPC_PORT)) + + config = ServiceConfig( + image = CONTAINER_IMAGE, + max_cpu=500, + min_cpu=100, + memory_allocation=256, + max_memory=1024, + min_memory=512, + ports = { + "grpc": PortSpec(number = GRPC_PORT, transport_protocol = "TCP", url="http://foobar:9932", application_protocol="grpc") + } + ) + datastore_2 = plan.add_service(name = SERVICE_NAME_2, config = config) + plan.verify(datastore_2.ports["grpc"].url, "==", "http://foobar:9932") +` +) + +func (suite *StartosisAddServiceTestSuite) TestAddServicePortUrl() { + ctx := context.Background() + runResult, err := suite.RunScript(ctx, addServicePortTest) + + t := suite.T() + + require.NoError(t, err, "Unexpected error executing Starlark script") + + expectedScriptOutput := `Service '` + serviceName + `' added with service UUID '[a-z-0-9]+' +Verification succeeded. Value is '"grpc://datastore-1:1323"'. +Service '` + serviceName2 + `' added with service UUID '[a-z-0-9]+' +Verification succeeded. Value is '"http://foobar:9932"'. +` + require.Nil(t, runResult.InterpretationError, "Unexpected interpretation error.") + require.Empty(t, runResult.ValidationErrors, "Unexpected validation error") + require.Nil(t, runResult.ExecutionError, "Unexpected execution error") + require.Regexp(t, expectedScriptOutput, string(runResult.RunOutput)) + logrus.Infof("Successfully ran Starlark script") + + // Ensure that the service is listed + expectedNumberOfServices := 2 + serviceInfos, err := suite.enclaveCtx.GetServices() + require.Nil(t, err) + + serviceNames := []string{serviceName, serviceName2} + startedServices := []services.ServiceName{} + for userServiceName := range serviceInfos { + if slices.Contains(serviceNames, string(userServiceName)) { + startedServices = append(startedServices, userServiceName) + } + } + actualNumberOfServices := len(startedServices) + require.Equal(t, expectedNumberOfServices, actualNumberOfServices) +} From fe3afd1a1d4b0823f41765db15c1e19eec0e6ffb Mon Sep 17 00:00:00 2001 From: Kevin Today Date: Mon, 26 Feb 2024 15:20:07 -0300 Subject: [PATCH 100/102] docs: What's New 2024-02-26 (#2217) ## Description: I want to be pushing "What's new in Kurtosis" every week. This is the first week. ## Is this change user facing? YES Co-authored-by: mieubrisse --- docs/docs/whats-new.md | 65 ++++++++++++++++++ docs/sidebars.js | 1 + .../img/whats-new/2024-02-26/bug-reports.png | Bin 0 -> 13219 bytes .../whats-new/2024-02-26/enclave-builder.png | Bin 0 -> 487455 bytes .../human-friendly-descriptions.png | Bin 0 -> 142977 bytes 5 files changed, 66 insertions(+) create mode 100644 docs/docs/whats-new.md create mode 100644 docs/static/img/whats-new/2024-02-26/bug-reports.png create mode 100644 docs/static/img/whats-new/2024-02-26/enclave-builder.png create mode 100644 docs/static/img/whats-new/2024-02-26/human-friendly-descriptions.png diff --git a/docs/docs/whats-new.md b/docs/docs/whats-new.md new file mode 100644 index 0000000000..f83138341f --- /dev/null +++ b/docs/docs/whats-new.md @@ -0,0 +1,65 @@ +--- +title: What's New In Kurtosis +sidebar_label: What's New +slug: '/whats-new' +--- + + + + + + +2024-02-26 +========== + +Enclave Builder UI +------------------ + +You can now build enclaves without writing code using the enclave builder UI: + +

+ +

+ +The UI will automatically generate Starlark for you, which can be viewed with the "Preview" button. + +To enable the enclave builder UI, go to the "About" icon in the bottom-left corner of the Kurtosis app and select "Enable experimental enclave builder interface". You'll then see an "Enclave Builder" button in the Enclave List screen. + +Kurtosis Github Action +---------------------- + +You can now run Kurtosis inside your GH Actions CI using [our prebuilt Action](https://github.com/kurtosis-tech/kurtosis-github-action). + +```yaml +- name: Kurtosis Tests + uses: kurtosis-tech/kurtosis-github-action@v1 + with: + path: 'github.com/my-org/my-kurtosis-package' # Can also be the path to a + # Kurtosis package in the repo + # For example: + # './path/to/kurtosis-package' + args: './test-args.yaml' +``` + +This can be useful for integration & end-to-end tests, and can be paired with [ImageBuildSpec](https://docs.kurtosis.com/api-reference/starlark-reference/image-build-spec/) so that Kurtosis will both build the image and instantiate the environment. + +Human-friendly plan steps +------------------------- + +The default way `kurtosis run` describes plan steps is now human-friendly: + +

+ +

+ +You can return to the previous way by adding `--verbosity brief` to your `kurtosis run` flags. + +# Bugs & Feedback + +You can now submit bugs from the Kurtosis app itself using the button in the bottom-left: + +

+ +

+ +We're very interested in your feedback about the above features, so let us know what you think! diff --git a/docs/sidebars.js b/docs/sidebars.js index 3b8dad028c..60518dc9a4 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -73,6 +73,7 @@ const sidebars = { 'faq', 'best-practices', 'roadmap', + 'whats-new', 'changelog', ], }; diff --git a/docs/static/img/whats-new/2024-02-26/bug-reports.png b/docs/static/img/whats-new/2024-02-26/bug-reports.png new file mode 100644 index 0000000000000000000000000000000000000000..e6df0e7ead2a4504b9e1907049dfa26169810533 GIT binary patch literal 13219 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
c0C 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 LhDUIR9WVY5ei@s5 literal 0 HcmV?d00001 diff --git a/docs/static/img/whats-new/2024-02-26/human-friendly-descriptions.png b/docs/static/img/whats-new/2024-02-26/human-friendly-descriptions.png new file mode 100644 index 0000000000000000000000000000000000000000..3f132a84ac315aae8fa2767c9671eb801aa9f636 GIT binary patch literal 142977 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%0DRJ^S0UDoH_(y$vtu?&JMd)-V*X(&uj_7Z%a|C0 zH!obuafiKntv(jG`T5Y;N^W7h7LynF0R)D1@h$gJEF8q$hvIp2cvfo9(t-Nv%D-eF zyLL}~rZ<}BNb~?qFBZrBpy#HC5l6v>xE&A=W2pqHZ&Q|Bf8~~P-f(W(V9-s-&*wTY zGT@7cc=NCD60kVQvf(=x=xr@^GfOTy#fW4ns&yxRb0e*}8 zA%XrJXXbStwU0fBQB-fAwQ%iKh1javVW7 zUILCsg16!2b)Jn37hiI6z3u!EQ;CX_{oX_B7s!N#c1oF(0Pnn#6!k~hJDk(zbz^$> z0`~#h5k&=Ao4;A!MvKPSjw8|FwG!a`>F^zhBho)#b&=hDeSLeyoLRp>-8PvXUrTw` z+i;1mNc+5&C6Ly1qd?^F5MNTLdpNJ$mPmyh?)&Y|c+=#OQC?<5$>~X7G&e<7Va%=U zD!wHXj%Y2ZC}hq{7JK~_RRb~#O@F-Y09vauo-&-PXlUG&HLiD}^}*LR1*iB}%Lger zQ#Uz6`7?!;{g+rzKXA=RluS={Y*CN&PI3121&_WqoYg@>qa9WXj4Sdo=)%ow5aeXZW^+xx_)b*@;F=##&9D%X)1(-56~nks1)2 zM8n~D2%h|?942Q=CS{CMl`!ZwJhPOBsp`3_A5{G~ip37q>;Fm@{i6UEfo7GVD?;H-vAv>`?)y_kU|nZk}* z^GbMxf5OR~s$jD=xp~{$ewqk#IThE7TQuAMxid5aH?fo6*+O^8d$H@5=xN{Xj_zIw z^w^DKt6v-+2eDi5ceLInN{Wk-nl$_UfEav!%?FXkAD5QKnmZL%TluN8j=AXD-rDar zeMZ87@TddVH}Bf4`)j*tVrrA8A=-q+;rD9J3&9uN{728<7!y4X?~JPYy-|n3*JSM1 ze^ApFUdv{lQWA)-PO6%gbao;1*#}A!mA%9B9EP+9uy#=&n>sf;0vnF6wj(Xq&<^QH zh7S@t15kWbTJcD|wz4Ul^|*Ad4eO+|V(+2&UB#^7=TB$Sz>w15&v)6gZk*oGn61dK zEt9mv+3tfKd@2p_EWnTZehQehW*(3x&)}-P=*_k7+Y$DH%qv5?tz5lSf*(I;C2N9(1rxAORUu(1oi3%)w^8q4VHk{WkVMI3FNL(fUM{<`oQ7XfIG6+t`kwovK|xN zhF|M4tV0Ztf@EKKCKnpop1t3rj~h<0M89;XcOr&wUoDA5zb@^>+Bb=e?rEJ+E)i0e z0w}+5-e=*RmKi?MkTG?_S=Np$c|12R%<%K%!58v24sTNfv;BR`qz*UDwCb6hn%=x` zT8)H#b1%5>gUiJHqywHD@cOuGTw{BuC@9>`4D!-RJ_g(eP-3gYHU&LeGDc*U;4N1} zGy&IJebw3~zf|-547i*k=lMLWk;1T}vQt60!-d1sI^gWgL_K!32Yl^kQsxO%jgz0g zGnhL0p3P_4ydGZn>gtS?RNhcEy-f@M^8d-ijzDc*e&=H2w9=ApON?b9m7f*=wyO`B zx4uNp0CVCJ+tAjbCxWNF58`=@X4=l*gN<>=_4`5{E$@zlAyuUU*WY?A2AouiA9Y#! z%>m^}(#*eM8zu(I`4jN!%*!lsmngM{R0{Jb?*~6eP2^}_lh^v3B_byLTJ5EQSwXh1 zZ}eK4d3jy?q|-|?@3wl!#G=J0QAh275f(J@^;pB|pM+~>F{wKkZN$bI8hE@Tg)9pi zAGXfuNjBT0d65fW_doca>UIe+-E}L-OY(aJodNVp84oi||HQsqY?>g+I|-biD{(Zk zNvC^Dtj2;8FX#IZqPj;1vNT-{6Ym|BYba1(wC&BCv^w-m-9(`M8gZF!`AFdNdXzB^lk{Y<*Q-A}8jU==*hp9$DG2ekVWj>Me*OwL}Vt1nN!>_wuEjd8(8$J*H5?2JbNzB9F;YnKZ?j%z!@#-Asoo6&8k zQwyIhM5r6A@)r|mrX1voVvY4MyQ2ODoUcoD!M)(oe>l)$20UwcgZ2uqyBVp<7~YDg zv#Tq(fsvnQuJ=fUW=QAvJQ1MRuqc%oMdV^1~{)jEmH9gCYelJv1j=vZ--on63Z9i%D{Fmk>etyMZwrHLySlUs;=G`KG_u@Exd z(bkIWbIzSfGgIfDW8==?C-rDK@`4*vM?O42bgB)gFo$Tw%WSMFZFz(5Xf)1}{Lv%4 zGAYN%`h%j}GGl0dk1LK5Sfq>XCn!deVjn@R9RmxA=YrOmUJhTIfu}p$CMMJXx5xaN zI_=*QJT@I1fIlC{P+r|(EU};68p>1qBAA|o5O;#1M-85iO>_7eO7-F)hGrA*LvNq0 zMBmQ_cpkpQz}`gchV z&M6kgVOwq+OXElehZfE=Y*EH^$8I^)wOHzE^PQm`0bAPB^BKSFCedq<8MTmV4Jnb{ z7VnIV{v4~T$}2PQF827H&e=0MVf;pUf#&NnkT;WSjQ=$ySdcV1yrGhUcIIhh>qCmU~B$35LKX}5UQd* zGCQpE<8B<+nX|!n>Wo7-3~coi0j}HcZHVAnm@#3O0W-!j7JlmovJL+XOPE;c69E%|}$_u#qRn>s=)R?&> z#o4G*++_W5jo>EhS1tlt-?KeOoIcQVXI7Pq2JuQ$wwG#xpCT=NZ@flv%y)uYnUH$k zG=;>d?j08vT#K(Kt%MA=o!cvZHZ^27sA&+LIjlbccFwECdOpcnrQj1;{JwQAdwllg z-d&zY7}0vqxdOiHDF52TIBZAa_FD_t@%|cny(4 z{=9_Ss^coc{%H0ul4#3Dvpl~y5#YaZU;D4XA;ay)VNom9`==vX7^sf!1nsfWu_C`? zY8gnGtH{ces+aO1W%&#s98Lc*JNZbm_2wo{oLzmMb>7OfScfW5E@3V*JA2i79E04D z()PjFrzW95{+=KB_9pOih(JjbfJ3WPC~?i@hFW?=pij{ccha1zttK!E){+Da$3Zt! zLgt5x>y*a@Z=i6$&{mV)+J;5AdH0ypqp|9jd~>!&FGmDSR~(Qp$%pf~?drpZM+wl~ zZNStIE)S)fe_Ub|i!+{4;P3a(B|m=o;e&yj$uC;Gsd^Rp!Sf0FlnJv_zk#zfGPkEM zef$pthPPrtlcWuV7W00=p1VrQ9M|o=uzsDSY*dcC<1E z(DDs(-{<+G1*R_)0GGo&n!)o;VhA|i@4eg0zhVpg&uyX=q&_9zpY?-kZrj#ZHmk-H zW9V#Lw}PSdl^p`R>L&x?{92IFzvDeUFO4w-b8S}PjsZyn zU7wi-M||fhY+eOdImJdCJIq!@*v0BC)G13Ih4*;2s7vpybQ`}DP;Ksd!uFSR<9~|o zva%*AJ4T1%SN6VZJ5%-v=3>fm;WKG3Ig5Hv!{F}am}AOm7HB9bnUwxu1q?4!R-rF^ zpb@k9ms$VE&#TYv=eLlEnm4(KL16t;OBb;_y98GwV0*43j@a^{lzFj5>MMf=F7Zk- zslL^i6Cs61Z^M-HSKqfC&CB~7L%M7FNs9!6{wUo4v!ziwP}=(zX`5^I%=<5T_r?zi zJ*1>L0?shpjhN`IVczWjI2zX9)T4kT>1S?9x1hUiTJwku^+3tu|gW= z7e|gqSER=Z#3iqbkM;BR|CMu;{UKPA7st{VPevmzc5RRR;d2E`B&)a zLx2wRUG34$vx~Ra&V7+oYSO5d#J|ApFhkekqO=tI>)t;->wnzCUr{S(CP57qPv%UN z_M;E7;&h{B_?p5wdvL!XVL|jiPxwEN?2jOH@o5U~@y${FFDw2d7`_&y%_%Eede`h} z#FzhNoqv2074+Km%5%sp_5XLdZ+CA~QSpS*M?HM>FIoJ*xAoU@T>x2G-awwZ=l{~0 z|NDFY@zJ3KL|w|uin)FNb&-D_^)-S+QYxxQcm&@+1N{GGP37;hdR+LiG>XH&e#He5 zGWJatF})pz4F#<;^HuNcDF5m7n`;Y5#s`%=^Xy8-`Mp!CFI6NxcjEYrmH9O!23R04 zUX^IsT6@|&H$+60`-an8i;s;Pg@v0M3X@zz!?yZ``jswbs4zZX;JRbH><#Y^~rC>h-=Fa(8jg;lrTP z21r01?@fLW_FPR~qQ2kodsPrE?wJnF0;;oXaQQ$}?o|=|ci|)WYZo_O4#yo2J3d+M zfg~%%51f(?;sTnLNtiDz4W9VEeGAwcZ~XnlY4*1DIJdw~&H8wgZe6KaCSU~XU}iKB zHyDy!a0y6_Udv2rSK@dYyPWLxwY2nhm9=Brmj|tSJ=nJ4IueKZnzWr(edev1dM8+F zmY-*drU36==IsBXW7^!BdxRE1Y>m-Lfz)GWO8bfYgTrs0X?y3dQlE@gyrC8EZ84uY zIgfW=*{kBK9)8$1o`Bzk%$+*ry7sY`B1t9X+qRU)y@P6Qhfiqm4ZX~7!UFJFgm$@^ z)m$aMJjrr~U;HQ;SWmW)eeZ9F_CL<3Pb$cbg=(NhMgDVtb9)}n7BE$!Pt82DTRoDT z>_sum64Xw_t#?=avNYKIxW&iEZ3{sYNz|DU0x{G%@{ZFjd7Y3xUC}a4}3+ zMchL=R9}d;MWb>o`E*MSB7?H{-LPA6ptn(;|^CdK={FMl$n zicIhOVR{2A<7B$FLcLLIVyc>Km?<$`W7+q*L~rbiQnhIk--3P56Jrix8$@DCnA5k_ zshxUfQ+%gp)fXnHSs=zEs?;PAw9=+q{Q9fYqP^y7bJuO3txo;5Aj)zm%|BJT`C$GTqFlG63UVX&1thk%r|iew zAJ96XG1sW4t!LP{hV5whssm@NS&k|jz9=^x3aOCyN zU1Oh5#QtuEJT%Y)kuVWRv-@+q=|0#2+<<-H@)@9D)aj$dT_{B(q%u^$5y!tzfZ5_WN zi!%MUM%AJqr{;jBD^Uq_ql0Nf&zI}(*X~5A{qA`F-r`5HnHeiS-xGVc3lZ~5g2C*M zP+~l5`NsK5HAfDab8iTKB!dLCdcN?Hc{d0ve}a;D8Q1qG)vjM<2a2}9_AHU_sd|Ci zeLZfcseE~gkvn}mq#I`k*8LdzNmPl|UNe*P=Hna8v)>L(&Q!Nr&-yIg*C6%u^o9kb z>JR1z|DYo}6yjGejc)*;HLV#g{xl07S$+k1>;f`tYoNu*{;u~qC_o{4wMXR#b~_M- z)Q=T)yq$%~_HQbERRPQNQks~XOI+(gN-7c4#9ns$`z(i0Cij02d&+AkHvQinyeuQ} z3Pt#q*kKrj9eXeH!gCq4x7|E}rM>PiBDuY`qLNJ&B^wysE=ftFUFvr^V)8#p;!Wa# zXa?A`yHv*ZM&!Bz_zd%aJ3Z-1rsT#f9s3z_o#=)v4Q#V58?nPn6`l$SSUU-MO;G<~%$R#rSZ`$+Vuk zws&ge+IP|j093FowUqDcn#0X0=lAiIJ30Rl&|wN%W2=%K0=y%Fxz`!@gv!C!f~P zP&E*losC}g_AP?W=S(W;6ga(S-?bq;Zkd#Atc#vZ%44Jg@Y`WVcTpGaXRX2!3km<3 ztNe$3{>nz+`ZD;v>Z1VHmNPSt@dpf&*ar6e$og%!F(CWw-Zi+71E|Q6K;va9okA~j zI!}2^ljZ}kTQtnqoqoY1rw!(%{nnw#p=_I#BWrZhka?!SbH4yWSe*VLi3C4s!Pjka zTGiKtnUdS6SPv5S<=n=LiEM(73_i*8#Lck24bEUHE9-l7tKIh?2)AV^ur(=>>CpfXoo2 z3i8g`S;h74KTvfb)+dTw=owPvrdy8P=UgS^s#Pb4a$w+C^`v=`C)+)qZk<4g4x_gp zdd}y~+~dwa*Efg$5!4z3G^F!v>AMH*#X*5~13!b!Q#_fy1|Spf#4`6*Veb0+;@;ni znicJyES_a2O^l$LvGeNR&1a?0hQ3k)xlaQ3@Q~e7b93sR3i8)Io2SY3a6?`u)zL?K zO+Vke7Ijqkf0Ia)nhM;fc9&(SW&_2yc`e{!-G=h zXB9yiHUwk43p4NaQu%G_2RPOr_;05+u3wm)5Ipdf4Olob@bcbVHng)7Waj3j`5zAV zZ`(~(FFS_aw%94Q6g2fdBx*d5yCoF(Q(|E%vyQ>slkezvn6s&x$PN+ zJs}xh*oKE40%xbc(oVGlnHP3{lFrXRga`nBls7sl8wjZC+iD&tPw4)r8yyv_>;;@P;R!>KUd$@z57Ta{o1)r1@!#*U&S^ zJ@XX)g*wONF9I{N&}*8HBvsUNL37J(ch4lW`eA=?5Rp0%shTycPRDnGebfN;qkY!kr=PvKq1~h*kZ14on z1^{bKv-@9yKl30wv@tAhOSKXl$=(O#&t%0X5gIDy?~rwwM?LAGLF~*pmH4*yz@If_ z?%HklY*gb*!k*hCZj59}JEVSlW!j8O20^|824iM^Eb=V9Gup5{;1?3_xW8LBl$qg# zItj&uhgr|jpYNfHGSq9XZlq5$L^0CUO#7i~Lj{|kOgQm*d;zH0L*xWF{ttWK8P?Rc zwJQp^5m6KY0TC>KNK>lRfP#uN1*I32UINm4A|Rlmp!6P!^bR4E08!~J0YVK;=@0_a zOWIwY@3@cqJNuk}_t(Au@<6iIETheLykpE>X&M~@zaDluPr7FEjvW3h`CT3fuwEk; zA&_P*p(|QE5cr@+iV^d~drwhF85M@MyELRZ@-i7P7o{9!PX5JrT20f@M2y)ru@Rm2 zaO|fqtL*0QrnqOaT20j2DFcHgMazCwiJvg(-KB|F@xy#;hMEWtWS0vW%d({hB@Ir8 z`KK%T5uzkTp6oPrk2lP;DcF zx9iDlkJp@g;5*5=p*9z;ix^x}V~`9T_Dx+H%`A5;ah)CU?`dr~H&w4!S_gT{9l~$S zKd#xQqZiL1)~n9M^|%5pM;#gwvYKl6QPG-8(A`+hA%S~F&9ylt+C0-zLrXFJmcv~i za~U!7@c=B2Q7C>4O9g*{B0 z-ty~C)w(|OF4UenEU+wGHgu@FIV?XO8DJF;W?1QwhjEru7f?c0{;_KcY2XHkL&U^P z8^6aUxfz~16c1P7L+xM$!$%HhYbXnZgBevi3EqWP_G$^512Z=Y-K<*3s`kLmFX_I{ z=^VDYbHyllG==}#@E3(G+eGDRuRXsX>BPmLGZKn)F=?~^InMlXf!Y179FA7{Zq&`7 ziYCu_1uUxx=3?jRcjP^&P+D_%Afzhq$)nM)FKux(LMLBqrC)#^;jcUoS{^klWv}>% z_fBMcntVN(O;uahGBBygzU%RNyVvH{7O?y!#*a^P57-B>7&G7eZa&>b`))$L-2_|O zG{-&y{0 zp!_FnL_-@b#!R`3Y2UaTWiE8+PI9etd%vvzz~bu4dCQ-1pEJ_3zQvDWW&T5jxv-p> zfM2J?RG^zXqlGF^jA|a^)fs2!hN88wGH%Y1@D+rbVTTg=!AY5x0jvoVUoH`{deS^5 zel|q8u2?@3O(p6v0*J|>^uy6CH3W%Fl6D(55>YeB1gf2gyG!Vec&HAk|Duu|RYTO- zwml0wY>fL_@@Z~wf6bKwm&zGASG1BlLvim~oj+V2eW-0*?T{jD(bStHva4rezmn*s zrW>oKhEV?+%ucQPJe(3;c}k9Vv4$c$zhev~4O?)_^rTUU;B7JeCF#R=MwM0(%ca#} z7H0ROrgD(U1Q_=>bYaJK^XZ+FCSG03DD`^S*fYB4zcOWi!MB3kt}z=bYaUax zrPSqI@YgIQyww!hG1iN#-!XpFy>kt)CDsH8%^mk*}}) z?8mh~keVHqaro3~&qYre?cP_<`AoySHOpnVJ6*_ICN`)Ynaa>n(m|`YY)XxQokflF z7?Pc2&ba$08vgsdGbB&!pL#82ud9V7X=unUX4 zMAQ42mEoaphTlhJ&~9WBmP6#WSs|-e?b6g`MeG`TpH{NIkTmab%g|YuFTpbwreY=t zt#pVN(3u-uGfPUP)=2+6R4+Rle@Fw)JidF4Pmlhga$q1n`PBSoLk6)Lh|&1w zXBV=@s-0TRt_jo@(^+M?GgNBE{B6K5L*z>qU*o;BHCNB#HW!o$Uq~eBCN4MA`q(K4 zUYW_hF|>u>fhrLE{D|PKh-ASfG5o#DzF)8WJwt%uVqpP;ohcN2&v&aDg7Az)9QG@Z z9`zA_mc}fY0$x-x)nWbi3jYiw{b8Kir3=YA-W6*6h)o3CjySF-w zE>3KsWz54WNXHfv+yAH>%1yPPr`67ytApCLU%XD+n*V7iFAj zk77|dI4xgN2bZ4B0!z}M1=5qbyemi-rzb@?&4jq;=51_n{_anu zEYJhb63tZj6x*Q>)~yJzryHXawgY7@X5-aaqWrP1H{i?1bk%nf@a=lqE*sny3j4hvA;+c39#SZAam>D1j%Xx|dcbhB9q>U%xg{;D{^l z?FhFWcdY_$CbEU$OzJ(*4CAvOLby5t_dKe)r4uvWMXD@n&WIZoeKP@lGk>9JXPYBs?tKm0 zcqmv?sE)W!;0J)dljHYI0x$aAzvH{|rg4s<=y}sA#7zzeK@O~eTsJxmX>|9!F*stN z7oIyLCb_gOF0i~VOF(bGB;&``-q4q%1?>NQ#v-VP<+Ygc^R5yxCaL}G0^!Ir`asA( z1+@kFL(m}GbVYs2ji87QxCk@>Xm)g!!~W>@3G=6l% z#>DXbbO}F|P%t9FiFL9meyryGGJTnGb+Gc(9;i6lW!K{)ds6s_FNgwgT0Sf>ZEEvBo}uVrCrYf{Gqu+IiMX(6cgg&$-Y=|M7|(o8GCbS}Bhcg8g&9QPQM}mdNXS`#HYQG60ftKb1u> zhhiUd$bMBoYB^#r2}s8^Ei{q9+s0h^YbcAxzJ0&xK-Bf`8J-Up)s_Ckv9&>YLZoH8 zZ@vJyjUeB%;XHTI3Xq2wtH?+tRIA0a062m+h-BEu`Ne2`4+sNHx1%c^NW+9E2sK7EG)#4pjs^gSd1G4&(Z90$t_SF60a0Z76X0y*@p zCe0UDh&u=L51V1A{!I-wJ=H`>El!fd2qJ8Cyeu6I&fUT>eb>{$#F;p!uOs^)N2GboJ2s*GWoV(r%^Mi~`-+FQp z;(;iZTr2?&f?WduwIs{zs{V~LbG}=fnm2qR83J^)U6;j)F948dx(bl%l`7(r zg(KJ%UQOt`)H}D7?FPQ#)E;TJ`Qu8}91fr7bHbRYc<=B1_K~2uKOQelB5MMPiU40? z?n35;IUs^kTedT{vhR*A%oX}v?YLz}Dilc>!{)&t z>fWBQxYd0<>|>p|{&w5$WGPD9(u3{&k8E<<(e!7l`M$g98F)I?5XQT`c!dX)7o{GK zl{iL9N=-&O@cJ(glvK8m@)-mXFQ*UL?rW{qfqG#FzE* z?3Z-?0Np{*8p!M=$^S-QPZS{|qnh!o8?p?8pBwLUD6| zF41Oj`NZqLKi>ZH(cb`8MIw*l?thab~3zX6G)55tY z#IoG}kE8nej~4*L!rk%4`~UjJhYNt+imTJ;V)&DY^!tGRv;nn%6|9p=y!&6@c+DN) zesoRn&A)P4f!sl5%)m zmyzkVI#qYa%W3RvvhC5J8QOeMzvm2@sblJ4R*B4kkb-}n+0(ul%zeTz`1X7vaei^LJFO;sAxSyo7R`!|N16|n$`ZJ~ zpOAZazys~Fm!_j!Hch2!=#K}bORLm6Br8j(pww6L+Chrf~ z4Gd1Wi{t>w7Rre)Q#txi$rE+Ytfy~?1t>XLbEITah^UPDI?DdY7Ayp|h@xi4-0>}? zDu4>85V{A%ts;eKDx^%2_?x--CeJ%e`W$lmE$3s00hj5%0Q-rwVO;hMwhfk09e*qTJSH20k&98~Nw(h$kUOjOf ziVjos=gWi7z5QvRAKK7LiP)LYuA00gg2ukiMg21Dybt5)0z8wJXj`O3jD)L{4wX3x)iu=)HuYK_N3Hl~p5*iH zAiZKzb9+DeHHTGNcC&R(m#Y*Gm6*(wdWF#Lx%4%LTLG15SJ%yQ@sX^v5V1-Hw;+1r z^(O_FO-4#TdR8&EqdE!Wm`Ip@jy^aBV`#M5EmjGh#C1$Ypxdue5lnbBul0EDaYSdK zI!nYF`{!uRy1J~9>*KZZyIkzTAD9>y#$gRP8X5sAW=Kyid!?rtgOVdIBas9z1q3UK zD$2z!^3Lcs*vK&Z(Up|F&FS_qM7MLQP{}EttFpZ*8m+~`dSP^x&cm8U;w!cV{h!{< zNjd^|VoGeC?zC_TcyK??aX9uc4LMpej7}9CA1-4-ji5R%?Hndq4iZK?Ngk4(MfD|I zzTNR-00?Jcx;bND3aJ<#vY!T)q2P+iaB=*O`@$=*ewm(aL3GTO6ob+aOk0cLC0My= zY(uD%X@lWryePwz*s9%>Z7Q+Mfgm>=xa@-Jh<{SlV0+#Yld(>HyI?f%S%JDuDR3yo z>^5lSraRv-V+6zbMr~FOjP<5l2dgHg0#_@a=;bCZ&e^!@NXDV_(wt4^w4r3F{>5^B z?(RqH*vFx$yNSHpIS&SW)&p0c2R1D! zE_~t#E;;01;4rPTi7ZZ%ezHK_b0qq=81WFQ{6gq;Q6}C6BbnLNgKJ@fenWGem#B6` z+3sK;h@7u4W%~!QxqC}S|9~_I<0YLf09ByBU^Z*lrxlMg93Et1=q#wv2S4T%sT9qD zEulx$%XwVQVpCM;K><~*Yv!5CYQoluVqJW1s;|91#2*|~=RH&bp~y<0vvpL1MP)I#5)<}L ziRo=HHIm~;-KRlnuF0n`hKXOce2l&P*t)+m=k5{*uA1=ZUdp$=QwO_U-z!ObH#6ry zvTVeHT*h<{ehg-QL>?xOJk^^PDvKWuiGl~^87653&hsVx*p7K*;CRG(8t)7rk#jqy zxaS%Lx(;pF(x1C7KkeG_aQW7p)mVjRQG?X}LzA#T(K<~Ww|8&}Xf18^AmwEuJ>x0g z@(gYA26|k4_xth56t%Q9@l~Ax0>&0M+W?xQMZTZLuPc3^zj`?RLa##PvedOL%7@uW z?LTsVjy$r^k-bXZBUc+F*<#Bsx<@vdJlsPyO)LkjEDtQh zpXoN5H!dBQ&`2POKWIW8p74*lwL{(7LPw{KZ(9G*!#tShlv-`#WG4BH7DyX{eo)S2 ze*UNwLGeG}s|;}3cfU5YBeuYh%!*$`SI|`KY%Q?XBt1r>PbnU2OIaL{+zKchDvVo& zb`x{~7%c76;=~S>av3z`mR+1KJ_c~k#wf31tYA=6hsLk^i5%q0=6rdV1nX>QgsJsn zh!5m$@2xMP=1{1QFXb*Q zzOOlN(R8j<*52xUK9xa>GK^TBm5L$?cvy=+RetgFU+ACb{_p`3I*Q*u_ZdEmfRd&l zT+kffu_+(eRBrVhy#j2hnIU80{U42DhvD{)QDFYw%L&z19OeY+Eq zhSeq1kf|m1#<%s$EI@Ekx2)$m!pqQhk!b6qs<h9W za}B`Nj67ubJ+?) zi{Clm-rj1)lh;#^|6XYlYe0jcxtWJ-A*5_+3grhoud@YG)0kch=6ba*uIctMP0YWe zGSW3mml+F!roAHUZuF*YTYE8LSz&KJo>PbH0{PgrPa-*~pWpcVs$-3Y3cEfNsrDhy zaB|MuuZefed^~U2=Wo}MP7vLYg#Px#Z@m)F#tsCbhIcR8Y)94ZwzI?I2Y836r46QD z^NZ}A>J|vcJ@@{BrrfwRmXkyR#ov0johM8>WqW;bj@O-#yo!W*aCn9F1nhzPGfavH ziIv}_&m*2u6b^>w<~>f=MKR=ia{4_O-GOZkr%3bWPM;fkIrXk)>UCTRT)O0yWM8%l zg2RO!@tASu&~fiQ(abx9Np)%e9`3B5vE$i5!RgHp&2M9G-bY=vMLkNtIy^fKfF1%q>hMTvBx4)QM8z2%WOrJxd0|8%0P~-5%UIb(h$-Y$-N#UhkAzW*(wU4G)rId$<4icmoXN34UiQbaZ&@&V#Kr| z;II+8lL$%YQ8A~gVnDAKQPYbF7ElxfvI5PT+HhLWp=^&39awk={G7Y4I}39}yhm~Bnk~%Hz+NLm46Rsn|AsIQf)+;+>dbkdLqKVkeO;P1WF5)A^PSQ1JIXkhf z7~ThBM|hF^DaEnKqH~VpX#dEf5dra0W=EJ$!XEO_7)X;m9WUFPVwF0d>kBroxSfmI zFg8S}9EMt(etpL&U!Y$!jQ%7!B_TRApg7qqMLigzV3*u-WF2SwEMgj z5~Fj1oFB!{q*Q5b7$4RYFAna}aqzUm3kly#Z^54o21_Js~@QXabXD=>FbhLU`$WDC8w z0P07>^3)H+L!lesn|s}p-eC{{`y)N-#xQ?!V+QhJU?k~9bM>k_gmbgRoF|hv2N=ENHep&3>?!Qs8ieNr?aJCumNkKn#sy##RB3JRsvNhn zamf4R?NrqAn4~;SS*NTa8an^fyUx93TK4ROJ(G417oeP_iG(Rnv=y{Wp(G z5k?VG@SaBVJgFO8Lapvp`q^#laxy77#M|@SucR;32wZr~uOP?zu>zyp|LpU@&I6;UTiH z0O4$B)DR*pQUTTBi`uiWf|xHh0`XY;+iF?sa}UvopS^FgZXmlbb< zjY++If{;)fyN6NXthUrjb;}D#hMXpmoGy zhMaIM+QSZ$ZAMlmi=j4In!-F1a&WjlemK(}z-}J$UlfSmAYS>n=)$}skOsLpb_Yr4 zNp%P_Pv_~ZrDVove1A3aKP~`J|8wH({@2oBZ>Z!mOmGE75P1sPPihd|(uFo&6v9Ew zVbdy9;7$=I;XA+K%0c_IXCWrV(>?CS3w8 z9=Xd!lq0~eVwZhlrpdF4+6v?LGAUDt*VS5^_T~AiegE?>X ziB4?aLzbs*jR{CEeJbI&thaN>^htNV%gZ$lL7010GaWJUsiPq>n^lN#L#4TH!KYt$ zYJl$%(rM6bIZ=fWDL(}tT)9#`j$q=Ny|?I2+^#lSNwieSlOy25D@~=Wb?xNR-(-+y zWOMzwn3x^dgS9idIY652M+P$z5Dvv-@B+z6D3r^vpizLi{24@rw>Qn8K5w>>y6qeH z?%evuw2UonXb1bv>+gc#bP45MQUErY30DfhPzxE_<$6JQ43wy2CXi(btC zs9>r=A*fLaMLHzzFcwnsaPqhfABgt*w&=df&0W&AKb7y5$=5dX6&D60emJgs3n{D! zI{WpxC1>~y8rin}rV#+M$HMvVjp z5!CYrqa=dMc^gwWTuRXF0^pXb7!NHroq>!L7T%NNO6JYwX{t62Te(cHmuIx^=)_H5 ztd)U>O4y7^Go=P@7}@zgWRWrznc9qJd&k1=G8IH@_5Y?gUIGJ+Xv>PUcqsg##mS8Q zBywCnN#DEokcW5nqQ9!!E}WUMuv^vt!%NB=B873u=i^vQAnE)bmH#vbly?&8z3#@} zthBWy9ol;j{Rv4?i*Lt}*39~uQWYGf1?^aKw+@R39+#La5(_X{Gxalr?>A+f%g0wh zXbAe~KX)bw*qI;y+8Iy{3Pv>8+9HyEY*Gal6$Z%5J3qZ+BZ?iBu>!3p@9ZmpSg6G8 z4o@LljLsf;cJIKTMf?jLutCA$*Sy@tFzc1uq-$*9alfUTV$TM>I`+Kx)U6vH4g?(q zbz>e^E4!(reB->*v_WM+Pj=Y}7c~JLpcA|5>4#q#Iy@usq_b1761`4Q6W81b))b-w z(63WXl$4u$&SXXV&jY5E3X=IM2kCBB%K+w{tSu&zn)Tg2tTcj!D51R65 z)Xr5Xfl0J``R0nG8Z#-~2JWtTHRm$=^k}tb4iI)a`Pu~9)$2=6-l0!JByS*6Khd;Q zLwjD>Xpl5(U|8$tz5H4^ZoL=m`$~`;%`Wfl_7algNu(HHpMB>gU7iKwN>(>DSH=6; ziet%oB@pt%H#O(BNZ8W4%1Xwz#uEhQ&#Lu!iz9i=-fU7S(lz5NwZ7xSCDmBDg>WV8 z_FlA-Q0jcYIFtW@ZJX-5__KkKyWLklS}#+`XFSO2)fShBfg5V~$SG@hp5Ci*-T%;% zdP?4aXustKN%(N@URK!0@#AJtUGh&=QS-a%u4GKsTG*KBlwZR0U~Z26h4cEH@#+N3 z@F$M#R)ozhk-Ra>tVXFtG31p*rN*Kv%IWS5)zhg+*rpMyl4S4BHttnKm@*0X)a;T8 z>q>X!awV!sIK|PH#q^J-wPn>Ib;MI+4Q@H}{V&ytv)X?=vcJ(`PskIY!zNLDkQwqV z#f49D$QyLlt51)@acXIZBS{BSNOA;3? z5}w)KM_`kO$w#)0<~&9~u+}L5y=1MF(BL`%2e}D{A@MrijFo-#rEfUvLPdJuL+-AE zQ;W7%7vBt1JplRFCvU8~n<$;TB7#s91wxK~Le-F4e_XbBz(HO=jT*UR zv?K5Yql4OmS)hFOqMkr!CqYKzeE36wqK3JMKVk5f%0}$>ukq%J+=cJF&Xt7)UI$IO zS@yudxvi18xx}3su_;eK?7&wS+=w&wE<39_&bVMb&Vb#hl##T?BZWODx~)s0^|#*e zl*XNPT3dv5dK;WRJrWrBB>NU3bz?x<4KlFz4f@V&>-QI08_;l zV-wjWKSdJB`x&}`Y_lKG_gK4`H6yDjN8XGVVddVttyk21d`u?~shZZWJX6_^8k-k6 zCHwe#($m>P->uaEampAX^J%J__Yp;o_Rd%ZnIzYZ)N}X)gK64KB+Rq4Dk-r^lQHjD z;saYUW5&L#bw7Tc{S{=fhqw~ar?fVlT0+lsN+e3U9|u<$`n0F&YN=Mg_kQtUc}l7O z5XOK4MH@lS?zo%eK9`#WF1EwNP9!|$ZY~4NO9vIoS4GL|s`xbfgCtBMUfDcMure4p zv>Jw4fqr^yzA@TrC4Dl}btvfI>eeAqke&Ej67Jzey0a%<+joly^!jW`0oM&u+eiRP zFMuqFyfkbFoAUCDS@!E%CN}PT56O8@Vyx}8D}j7uDeEzptvmT{XNGogf@^1g-%0lWk%Nch^DU9cI@68 zx$r{{iM(o!pE1(MgeYzneGeu{2RCCB`2KTcVHe*o=RG{-y((#X%Y$RTB)w;3G+}pO zmZ_W?#1E*Qf_zf}(wkXxF4ygtOk}^6vS5v=x-YbV)6!$(ivjtH0|V2LxEvJatXv_w zELE}}T_Yi4u~TFqM02rZt(PNFF3q{{$iSwqQt-L>@$fIms(Gp=OC=C!eY;QNOM1r`9$*8BJbQF%#2w*uY2 zPG%RH=1pKXfQBeUjx+#Wa?0ARLBF|c=4;nKJX9_|NMAcGBVuMeQ?mes#(_(AXowsd z3p8G<=&n}7T)J}iL7Eq#cl0^ar{)itZi9?sZ@j#360=XMAziR@l(5`sGC1re1z3Y% zUlrV~Re{Y=&v$0C=BouHq*r?yRDsLE+ex5lD`?g8d#i4h@0VNhYvk)A#e#uh7WNih z_{}&Lfm(y^WDcx#Tl(4wpw--!V{3IfD_*PT5$_Sn4$sUV@KE;k-1;W>LQ5FkY<2~B zQbZ}6PVrjAbr+DQX{Z!7moFeuwUnAa?(%Vd_C!6WlmB49TSv(0h$huCU8L7itsK2U zI7@iPXCj5eIG1I4*WEY(sHlzFj2C09u5>?9XElgfNFN7t^0UGDk=3H z8+AJ*tw)^3*z@fL84U}HotdzZIzE?THxQ3FJ_X4OoLZJBYnNT@%xH(jUtgebQRike zE0(tz=~Qw(Mo<~(oV&B8{lhJ|U~QNTF1nhkRr3JWT~;1Gif~SQchMF4v6)O+YDPdD z4T?h2eeSM!uAN0_MFfR+sCZ90CfIvt#!$$nqke#Fc-sI*QgfgDy@o=&yok_j^{8LB z&=~=ZQk7b)b~i~;Nk;EjA!Nk^H4?Skg&gJTAZXFEC4okPE|;RF83D1OC6flJ*=t)- z8aZQ>;~f;4X?`?&PF7mpG*Qer9bNf0PAHsA$bm*IPhYNx9y#O^LOoVMZI43Apv9Y2 zzhz};XwS00_WzOYkUOLwC}EzM+X`Z%M?UA)MZE`3N0{7cWhbxCD&h-Yf=g6`|KBOh3%AYx8Ci;L-ej<%FD zh@N%LhI!Dp`0>?DuXWt=-UqeaG9VR)n}ktTN9%{^)eyzT)+Vd=A2g1gOP0)E_SpE2 zw)!feqt3K-+%Y2L^vApWcCK^oVjYMbyw(Sg_(32`-jwfk^txkjrBx2MO+%3^401V5 zTS>~}kFt{Hvvm~tt*v>aV=0_wGEC7Ax3QLba~*CSg;?p=S4#F6?}*g~*o<=TQQPH!^r-g#l>-`eG}PscMn^`$0evRRoZ+^mN_XUp z*O>iLk?yug>1hRphm~B?@adM3gpVfrWzk|3ugD8N?UTAA@Kf&5O)paqH=%P;-qT)e zYUFJsbWl^T_QuqvL8su7w(c=~mE2A-l8JP(Gc}p}MaIvxaEOIxPsCHQYc!%Xk+Oi# zjDL!DTXaugFeLeGF%2vaVso~Zo3A2O?r=5OHRL&JPHLN6?j_GiB&~Bcw~v83bO_QF zvweC_lY^oiscR=g6z;+s;1H<)&N6IX$m3EA^<89^euo3ZgTp0ir z)y2S@dIG3s*tnJPO2w=q16wX~s-v+Wem#p^it0o-4^8VFW~;ZK#EQAwH4c7zVRd5W zV0msihmuF!3y+@!QfCAIkcknZH#t={=$yn9o~PrB_ze~mW#f=_k17V*0b%d^LfA|{ zcA!9SkrYT-ChU-9d&|A)Kvy>gLJZSZM}2x^y6I14O{humG+!x8n5C_Yg5i0rWIGst zPw0w1{N8O86*pRd{c7;7yvsbl#eLA`n>Tqtr`y7hGchE}w$6^opDS#MTL+UA^vVJL>deC4$h_rOc zHh?e09aun(o*V3=jSd^BsR3sXH1CqN=Jq>BhcP>+D-{+LUoNgjZ$Lh~KN~&s+q%=z z4zMw6WW1Yq)+2P`M@Od4l6SKa$yib8`@s27*_Z7FikZu$UN_mM{_$m2)^B_F9R(7J+S0%iKR$MkqnP@8J#-b(u1lp7rPMkaG5nDGeq{mk@QWvqD zRiyW3aS-pqKKgXj8C!2hk+F!YRpj~y^Dh=m1A)d8@ZDp41}}*Mbxld>@*^N@8T0(_ zpbrhLt^o7i`R4^fx?_sbk=W|>>XGi~N1&7n=cyPesAfWm%>hIkcU5lBxOIff$&&AHdJd=`%A4 zuy?>gp~P&g$w-ORtXD~Fa&NMRwM6>+7&h{cG?LpQ;a3N?dBc~}JoEcA?ki1nwa@^T z_?45jl$^uOM?Sv?SIWE(0or1a8yjqF0t1rzQpP&4kZ<|v;AK$slj_39MS3HYTmGe~ zl8xj*L_ayj&=r{aIKF^kX}`)iYiDC7n`A0?$VV0k2z&cK=FqB6g1$z9>MY zp8DbSz*m31k&=5M_1N#hcN^XH#4jF<$s(-oSs~*Vrai{`zGw0u7DQXgI0#FnNXB(Z zRa$Qq##Q=`iI3HLl_ac=9{(vO8L5B#AZDkU+G$G&9-RQGKNFEkawBW>=453EGT&II zQQ!&PJC?run@sWc0?pSnuymu1?s;<}X3wDmk7-Z+OQ(Frk5HS~F!HQ2kl zbM}INjDSQ|FP!_AL;{~W&Ii5a&^vn~_+KZszx$+o7T6&^nz-M%Q-2|w{_~mQKYJT4 zm@+>2?~nUH3+#%zGT*-_(tjD#FLH7ZP#_R5I}#lIFS6?2eY&awy!7nD&i~#3j@KQS zfi=21AA`+zCv<12MP$WS-F?rgTk-t+UYGak=p_?`)0=t`#$OGeB>+iz zI2dIM|TvwLWE5?}@j8N-AeBej||% z@nx01TMHSd*t_y{a~`;jJ_}c0M5pEG(+8qGx^+o(5 z-&}1>UwLFIx0vca_f7)+I=P0vSzC5KHXn&PTqzm=2GUoL0Z~nzkpx7a!mmaj9}bSX zRx@{#uo@`&80hG9ii;z=TE#c4cr;pY(5QY!A;Nyg`MWP=pH!t z_TEKbRdZnNI$DEz_6Xw!YxQ_hSK~?9B!{1k;9$@u&D2}{NlbGRGBO5h4O`k=FWqfN z0m_1W(OQB9#P^LzPD89kSrlUC!BsM83xiN9{eGwg(qml5I zep+(L*fd$fRvW0e-uu-eQ+XL+ZR^*UvgiEoN)}%5QO^AVwmZai6v_^$(U#=A;Cr~7 zXFXc}EGR)E#L&RpH-{K0XzwZekSwdQ_-aDO$aw{dXbY+9;Rz50q0T*MzJANxZLMFL9kvr1xvPrkM~{REIU)@gr-jrYZ| zK967%nfuXvIw70~a;rE1(p^?kJEM-v$)#`J_Hz(I*r~TSy0=je0qQBxn=gyNQrE+M z1RHOs5ik5^;+5||r`GWT@G<5}Z^Qr1*m7LYC<#yfWGPM;2e%%$6n^1(6_HRgorNIT z4}5xKccVoecN^%p&{H8|f*6gr%7a1b1Gf9`tqpO2N>l`GO@+X3YMdwQW8-{3zogkU z#9hYNv!3^T0rbYA5ZA|B0ZSZrB}zfuDdur33MdTRtU&?Ctvx{JV>u*jUd4S$0>t^F zgndXvW)dyGZ&K#*YKOjR zChHj%x!uGygDM*>`HPA4rqWKM%!oL|ZlP|@TeDsA-gJ10yA3==f$8-vbtHJu^(((t zvK4^!G@st=6o8EmKSN6|lLohzhf7kp^9r@oGR3*qljs3`F&{uuDYt*qX97!jWBf|aBb%naB19$mk?4( z0L4}~&SXW1o&}S0CZ8y6c3dfy`W_=UoE9VPJh7z7zg<9M0e+Tm=3H+Q!57QG^-mVM zvN2E)lM@FE`HhwSj|!59a&+*b2FobLVK&IuR}5K`C)g4spLzm&rxAXN`*sSqd(Tbj z7jDf=G97cBF?YHr$4T2*oy>XkE5zM_jAVrFKt_UBRDOYFdt~Oz(Ms#?)L!hNQGqTW zhqO~7xxb?6dCsHkyPZG2HxnGZ*`agvV5sR9V)Knld zN6+*X!Q`;eu!XJ?DUj?y1RS{auWb#NnwdP`1P@Nlae>d&NDsv{SCTd-?EZyzd!#R0@s34B%t36{O z;H;El-+M9S)0Em-GlH%7Q(fykk|H^B$IA>WzQ$w5&^iZc3YS4U0j0<_IpjWwdJy%F z2c+c2Vv+}!dTw<%f}*h2b)YSPJIlrWwwLnpsiB;=2H9Tofft?rY_@3?|8w=~FUI~Q zw*th5!0PqsD|=4|*1*Bz`amcFK4yuFGcEq4l7w8V@1}27qLSQwmomehZrn2=*nKGW zUZ2XMNpeTa-n$!l!M@7%==1(y?F4hb0-#O8IDJUE|C+2lFwdnvG$fObMys~{I##&4 zb+~34A|%$d|J+a}{9`zB7w6c@5Z0S4)r}Z@3m?cxr_UW60NPe1$H2Q)wQ!S}^b7FN z=oom48dzm_P){ect;TJ5c@fwho)cmAeY)*qZqYdVhwdoyZj1!_)}1Nf#_xwCaX4}g zeG8oNLEcb86>KXhE{;_?EJf4+%JiD<31rc5O^0d0K5!soHC9kh6?5f@y2;IY6Y<=Y z+6(`P>j9%4d?x?Aru5vuhsE$px3^5@ z_BS*!p1{fh?b}W|+|W2?g5&H>lVr{_Po4s-TshD-4C~GXxO+tqxWId*AaCV)O6tZp z_yydgCEVkCDEnKll!B7|)VclRk01h=4otTgYJ#4wI-pO6J1*pH2xq2vyz6%IhI~7GAS33-;hHxY|kv26BfK4WB z1K}wj2%tz#=Zo|PKj2gy;L!p!Z0(5W$r}X9qhr_5K2HG+t)#i0=b7Rj0N%(|Kjy&c z^T2BLx&Uu}Nt@&mpN&>_4RiDK-bLhkQf36ze1~{rvedt!NjJVH_r~RVG#p;(XkI&$X%9<)C)f~DjfXH;z zv%YNj7?Y;`Pp?5fnMIP$ehodPwEwNo#}zm5Vq>QDjLA84eMrHYJt0^R&&}}9b5j=# z(85jcUfJK70RK3kDKMl4Y<7B~s4nXY-*T_5Q9;{NXU;8OmR_~H%(i-Sc=Y{&z+ElP zcl`7w&(Hd6tv2!>V(p#}jykUX!KPY~k@foZK*TnG2NZOyYIV}x8?%Oo~+DfkvsrkDHvjRqcv zzdZbsqMs<;>|B+w3H$Lu%lbxK%WxejKKi`2Cd)^On#sX*-S+?i!RBp{a> zSE@7|L&At+-R+^D?l-fDMMm?nwh1<3pL{viTkAPjoP4VW9zQ7l@sVj&Hv28Dt1;^0 z^@X}Xt(!J`!f!t5r8~#-C2n~vTCb&CUvWyV((i>ZZaZwBrh(x5ST>y`#p$PCu5Yw* zerQO&`5Vj0=f52||6A}fYw?o>&4=TalF6oOtX~1%|K+;F_R8{862QB{a zXIGw1m)O*t4HP+rVN+V$s_s>ou)KjdkZApJ=kxv(mKQ#f6ujG66`>eHO9X#TrjZy? z@NvwWJ4bP%4EUiA| z__ciNxI@WTh0Ivlxh3V61uU8a%<;CB9oZhp>1$HE?l&EP&xHA2vTiq?yb4c#R7Hp^Bz^c%)CCo2LEMIfW{IAbfU*{OGHp1<|ST2nemsvaO2QX$X7ZRBezf z*LnQWWYAWxV^K-9O!;V}SzI_te0RABxoQ-sy;_G)_iHaOT|6bDJ%MvRqm2NQp}VtT zYsKThZakd|C5;cvc=c5!gkmqRo$wqo(wd*_UcR`rg|~s`rwuB3-pzIQ~25I zPs{2Ul$DY%Hjt*O{H{yNd&%t@L-5+Dnt1XJ!V1`)O9t6QN=}28j{i*l1n#QEJ7Ftb z7G^?DNG_Z9ze~Ts)6_7RKP$>>si_!5m9n}HuPR{XtDz_Zyk(`Opsb7e$7pP3p%T?& zEsFus%5#yjk;~o}Vl>Du!(Rz-W*}eVG-xlSH88c_=iPD6KM_b7EvaZGoj|hYMiC&0 z?IJ1IfCl$`wtQzONhMlT0YO=@fp4EHjFT|7$b*HN%egRffB<0B`M#=aWsAnDgDL;D zoi&i=m#FlP61WkFBJ4#!QIHqWAk!TNFW8Ma^5xXK37i~wT8EoiZ!BJsoX zJ4IC#fFRctz0HW7`I;m+CuRL158~nE!vF%&3B+owVZr@K-UJKQZ<+JlzA6-2Ln*qndD}qoL3M;hfm_&mVluRI+-VNwYL_1(6`Xi&QB=$ce3^gr!f4ZjYi;t+^Y-}`guegj4S0c?ipA|uIr`zm z3m5k3d^{G*-e#4SHl(7&S#r2f^4U-ad_;JKVuGOs1t`%OY$9D3L6bmkF$QJdT!VJ^ zkHY18U9g6@-K_w@CO^>!a$_HxjvqS4`)J4Ny|kX@{tq0l3v7oB1-4}R5KS{ug73G< zTO}a-)^w3ZIA))ZXxI-M(T_Sjd6GK&&60~R&8?tKgRF3v&v~*B+ao>md>1V9nbB=q z+-r)a^~}q@w!H60T;Fx@2a03LuW*A9w;hdOSe%w-f41t(7xOw8nYdTT@zxgkjo)X+tGyuO{#B z0!hm)!oZ2&N_s>cGIy#j=EM#>_U%1suKKNTFYC0#^OhxK6eoICEM#6=i>Qlh+HO#p zQ!}3Q-Of+?Y^?F2>WT$jyIX37X2zY)cP75Um{#?mlhrg!?C_p8(GW7Z9efVAybcEf zk&*Cp?%r=BSL-)*Wua+>XA2Sro+A%&#}d2%wo!=k;CqckPXGy^#AsOCo@s=i3*YT< zrVay`UNpbZllXzHImbCFMT*D%m7)!Bc?8P8N~;@)r{e7w=T8(`GKC`W$m(r@VYHXn zZjNYo)r8eI2@w!tn;ef2R<2VG+@6j=MP)4w3M%XSy&r!%_f{VumR$=Nf)+4zKQXcl zDPS1cp=jSNYw0)iA%DMxSQCRzPrPsrc?ity=iBjss6}UYB25QE0t@5x2MBEbxMPbe z6aY!B7#Sc{oMJwpYjIci8?{{5|89a?0x%kxLBS8LyPG?2K?c^{W-!n1eJyX>RVKYl z{RZ@li6x-qJJ~xQW!6B*zU__JVRBPV3B~&p{Z0x8 zg!A>(Ki2qNCTpz5dEv*{-z+rKq(?6dRhjB{I#*|LKC_JAWdKp{?OLzJZ?{g!^S<;f zTp9}RM)by-ZQf&WrK_c;mPvc;_Q7+=ZTb*ie`E0XmwNqb)W>IT`^cx+baGGksCe$k zFRH?5yNwN@QdJzQ0CFRfm;TIkF(YO%g|TP_*YJbnf2vUvq9{#~k7s5DJj`_8$7NWS-jI<`*R z#VF#44arz6VpyR*DgZCs{csSk*j?NGyLl?UKacS!$#ELp=~7P-MR1%>knxZfzsA!# z3i#gg#+`03W3aYKcU#R*wx=R8E7hKB1%257hwmp_SjMe_NJ|(k?J~{%TUNFdXoM>pXrN-TKKCG5B!O z10p^r!LFS1Kq{Olv5um8`19}!IlmWX<6#|0{oGT-Jfk_|nOVYe4`)_Lj+X+bUzP(i zP#YcEkL$Xm3?{8=2bOQ{6zd-O5ecCirCcxz?W^@~Y*7hwGF$KC*l@P1J`!dxJ=)7n#p;uK>Mg$B zwxFDDtfIyYR-%l^tEXq`z#? z(mCain|c-qg!SH}fWdS_0-A|x@&^EdS0RpMPiMb8YQ}K7kdfK%6id(L>`i)|+p+7l z*Lc&0@#_s)=8j(ua12{#sJE5B0Y4LKi3Crlq*Kv45_gXQG4oRRYDoWPMfL}}zl(0~# zh1ld2Q8c^zJv5j&V{V zq_QK0s`-sSp>LNYV5_9ok>-c(d}dKkgvJi-t#fq!c6D*aY z$C#HF2R{)M%>8%Due)Bd%WE-8k?6i-rk%`#G%gVi*4(xz-U5f(gXzLJ>@%U zl{PUDrVw)W>-=`|AjxXq~;S7ZJlyn87R;SqxQeB;6t1AEFkq=0gN zICqSf7Lg1j{u0}H!3S!XFTAs?3YKG;q#AVZl4%3g*znSYoRl~lS0;zzQ)J~{PHaX> z8_ams;-$G1004<=Ip%_{m!9?hz-Yxf+PSvp4TUaVnk?||v_8LJ126x9uN@jP>aRA@ z$i}Y#NOyH(+dYBz#wi7x7S#CzWCPm|gNeDc%z!N-vMi`eDq=T_tnIyiXqz~xd!7rR z!uz=g%^)Cog)-}Gp9I&Cy8eUsQF76bfgSQz0murUvIFtTG&qxKcJvwdg5l+8QDnDd zLr-bh$X2PUV`lp(+Jt&~4PW~cs0hSkm>*IWU$Wp0w3=kA*i1pkUbuoz5_h%@eCWAF zrS*kf&#mPmb<*?J(2q7~!><@wq+lQ6YqMX+&HRf`frdMrr-tiNj^xR$TxX&vu6?`l z2=gnQi^_5J4M88P)1~zLyu}0&PrtKrSNSte3xW(X~QnZJ3jV=ZR^R=v22`5$C4-OTPQHhrv&8h@FeO3~F2H z2IFEVxtN#WQ7yCDXHG8BZ>H^`qpQ%Ol1teJBYe5J#T6p8_f4Ez0n+$b)$8!(!$6dxe>DIg={_o zJ>M|?5s9J{1^`&eW>eTGlZs>Z%ru9r9j@#$Ts~(8Hghyy%BZ|7wU>!3Y6)B}+~KHP zwCdzO>r0_IsupmA~<9L#qeMD5IB`^0NSeE8-@uc zU@x-T1D6Hi$anK0$+~%%k&k_P~Qp1t0?!MagHGa&l~clH#;DAD>P4 z{g4{~OT+2#LlGbZCB^|2z!c5{sy`*kI3P}1r5ZqwuX$b?*5fT6Y~@%BDJ1E{gv&{D z$)gxK24e-6-n}aFZtCBps#|*&#hoGN0&utA3$-G-m>w-*krSl{~qNFzSCL9DGMC zSkBG)`~+*A{L>QE(oErqof@FW$VEGF-3HDD!BVogmPO(Ea;9)F?!0C-0$OJG4;(qPZ+X1 zl}GS%8UpSe?~z@D{pd5$!cnrM8QXcF(1(Y0BCwnVya_XQH;qRX(3@Rink+Lxb6YgG z#+$eW-mQV`8^cmWt!lunN!vm9uTUGuy6kb=Vj#(=Yld5l51>Ech?Bvs0K!p%QAeu@ z4_z5GOe@PD=m<4M*NuN22)t&YWt^6o_3m01o6z=!eCgo=FU@m0YXZMd-Rhd^-n?8D z4pgaXxw`ZaYE90`6_F8Lb zxd>Ux=MQ(Yug&JaG<&1fA~$>iD!%mPE6==0p_c~93n)u>V8@$1s>vVTTOf=A}y| zEuxN&TnyQ>A|dqA@@SQwHIHcST&AF@YKH7rhYy;}CZ{X(J|X+kD%X^@pKtDDM=Y z2cBYv1YE%HtyT2T0JdVIKi6U=pb^6G5KweNWQ+p}VIrS0nHs zkU?k>DpdGGA^hU;wnzviA~ZtvkKFC#om&}3=Uq?@za!_NWY0J4qTee8j$+jWO+$YeDH9A`O0Oqi{mpF^}Ax6^1Av0ZEGjV)PY>h!f8BB4dk24>Yp7&ID< z(njF0?{R>tDv5atKR`utL&H1BX{bTyhar@!eBw0xOW291Vdf?g!NsgU^A2lBwEZXi zUZ`QPAvwTXaMHkwG>WqJVBv@ib0tNh%H{xHC9}19q`#a&oM& zgwV2E5xR7^diayYbPzR4dp8#7(Pp4r3Vq1rY_J3J(+OzB%EZ81_#)MWFm2p&o*QCL z)DpEbgXfeQ#oVcK)s4}R=jZGYgfh%LT#?%)MNXPAIz)OQL8Pt7jHwMMGNv`u=HH0w z-Ph`(!ImGNmxZYN9>@f|!R;;!Zp$?0D=)BEg4``j0ex%v+&JAc|Ng?iLi>LJwf_cY z2RWTnr^VDR{1z;DK#tF{XuMEXB>ib7*DIGcV;_@+WtykJzp&kZf8`HI^q+`t%Yl=x zxfWhX9FP$?&lT=J<(n||&4zl~XK7GK)Nj#IR8HR`9zlM*Y_7RK|E{Rc}|-t>RJ>)GJ; z^_Q3a*G&J3&@Eruci;i>lW8aC-;Gtj^INdx>ld8=`*#7npRzX)DsHxU;J-@gU+CaT z4)yb=GiMI`Piq1mlA#6+G#Ai~yME(e-~6vadkL6c=w+qg|EVP4A^B0jz~K&KA;Z6@ zO8iow06ty-By@qz;D0Fn{}<)|hoa0F@ScNMa@l#Z{FS-!9tmzboI0uQrK9i%jsf^6 z%L|(4{?q6Eg?95t_e^BvyO8(an#hsX{QZABh?x8v%cp0yjgOB1oxT4Se8%_h3|&3L zjuTiE$cTiVzg-mPPi7tePn-UW$qu;zmkTfZZbU2p-3kZR!_Z&J`afhC{7fHk15J{4 zGKc?e>~rdY7yjdC{_5E$UmG3%+d<3ndycREY3JF$b$l45 zBP9MOUT2+Z|I)3<>&(<`C=NLX-9Gf^Z0;(umfbA&i03PK^>?o5wKgCqy}L>we>X5c zWzQgcy)F&^h5G)G(?11y6%bYgE1T2b4)l!yjI!R!Bb)c{jPkrNAh4@9W{>{ezzmN) zZ>`w<*7OgA_`fQ*Rq(mGxbWrFzqQ~Y{XOG!toz{lcg7i<2nea|K&b5B4y@mk`;@q% z@v%SE_SZ8#8KaI#Tz8$ciiwxdpUds-DI}ODb475AyaSao9o;9A>>7^BSAZB_d#Dd? zet-J&ZVl^Q25jB}LrbpqaOaixc!09mD6y*ZDFA=pu=Ql=#a3Ru5&UY&vdSye>eD0b zr?v$#wS*J6?cwdMxs>T*`sm1J|22g6mn?>mkh$WNMLlEGhicNf7f?j2plIHAZ44`b{xLuPKjsI&CCWtF1$17oB}^-$#}PsImPFNQ z2ah}sYBkD*B2bQ7)@8IYB$tgx zo(!dtB$@T^1y+A`-|xBhA-Nu`J=-bBbS&$5e@;KE-LBa&PO;@5-ZS?tNWoSe!_mw&rsRB9gmwMKO4N;n)PMRo*e42wK?=n3pPIr8lL?vVCkdY~mzy`SW z7U|rJCz>f&AdU9%y>ww*;PA@DC9ncK{c4eU5?6P8l9fOsco=+0!F|MbG{4yT@Z>q_ z!5HBratx{xtznX9uA)Bm5dy+^Cw$CxzYD{a?2fE=D9ok#_q;%&MU!uqh(E@Zq#O&R zsAS#pFKIx!QC8~`Tt+MMTqY}B-oMgzT;J{Qi5=BSR#+)*xoDlXZco- zb?E`O+c*CL8fW%{qm@(3rf)%hP5d^B!CgZ76?NT1hEBbU~^q~_G zV?@v;MnN~)lHUxjN&!iZRT3Zs${fc`fL;v4+_oWEGUb>|n&Me!+`KV&oN;>$QbSs{34p_>~leW_n%LJlC8J4#=fcTDT8 zInqR8&Z2^!IPbZBliqFr>Eml#hTpT{1C<2}pDrEm-C6dPu*dl2AzUcyO;KFex!6~; z0tr_MQ_sAcCpa>K2*v2J@;?8JMs(mDaRC#?{}t_kJk0Nv&-3)Fy<^*8&O&gLe~ru7 zfVa7QO6bbAE$jt+L%5AupPl;<{plUl`2SF z^O3FqQNCW^Spol;XEy{CtuVq}IZ=62@55!9Z8si<_{S++p-SJ%O}|UA7b_-b_{?nJ;!42-YXW7pHUBq~E0s8jMPx;a`Lsj|znIRjg~>=iwNK6Hmy-cHQH zZM-hRg}+!(=V5ZStGi#rrYpuFM!y7$gtV6!pv@;2vwS<)C1v`7n+R*YF7aw%x3#|9 z2}Jc0UL`T8>yRcXaM|Y`igR(2D(TDp8(2|Hp{@ny6P!8RVz48h$o;`QW)0R$%!MMS zN$kq+NpR$>dF@!o*XXoEdWbNa3FWMqqi{pO$OEsBeY9j zMLy^aaPuRXSODKdj65=81}&op(+ao&G^ZXBWaDw1vcjHK68Bp>1rfvAhM}BMMJN=01tZ zVuCLpwYlz=%M-w;(`=~(YClhq>RjOSSp^S*Xl_*+L(Vy|oT<0hic}!j@=zX1jaad? zUeTPUEm`rBe1HreK=s0NeN|4Sb_$N87xwsVYr}n)3fEC;nTO8SKC6m-X(^Sa))Q0{ z6u<@HeGK%d+*}B37F7{B8n^;UNa{yC755=Er}-zQg_PS60&$8dv|Z<|akzjSM?IJ9 z#34~*!J{e6`P*BLC*w^{)ajqUJF)ofi+2}%azFDE%T?Bog5UUdKGWM?$U{TSZgOnj z`Kq|yTqQPKei>c>p)suB)h~{$!L#G;ggS0jA0N6&ns$#8b?J!)$9;~le2vuWR-efk z-ze2fUKG1;#g zxc*_1P%7vk(}vekq$H68b0Z@3K2n~%GNi@C@HWIuc}D_{BV`TtBcAWZ zg5%e~wn0pbg1eT#gsO6uEN?MC?gb7NH>~-M6p^~3-&{{ZhQvMS+dg{WL1-kYi^pem zgA{Mhq8do+FFNvtjqL4*V%RF7I*4Jbj}9txylOgoWIR<`Zx%-Sc}r7F?a{U4XYNO> zBL|ltxEjOSO^;_~PSzLf69?-bY&>Xiiq)-A(fM}8KB31c>U!#>qcKxm_xmeqkadHF z{f|fL-;OW-yyRf}&H3{~^$cO7KoKBygxs{ZywMSyL%L~w-8;8Q_2;)Qt0u9w)M7`?ldSOGr&h`Eb`WBp9TnpgQB-gwG)uuuYj zh()2KFy_2_Ux&c>Q|kj%-fJcW6#VxcRp3})CY?A-NP*))?)2_*T1+)gaxLyUWkeu> zR7tDIhf5xdyG9v;13g}}@?xg%2l#0h(h{?FcTNpNCO`nPZQv=n+GtctHH`>)Jha?# zoVg|7{nEbvBjJdC*`Q+FP?&DXgP~m#&eqZs1AfPd&<#;3)T6GV!lq1~ZVy{rTX~^{ z+uU&n#a>2`koR+qEpsK%;uGllU4~&WuhcJj6EP8}JB`&(K>Lr!M|A&~bql@EJCokz zc(Qs+lkcDyXT8~tJZM_-A%Ryi)^(q@Qy_)4R~%-6ZrCwxg2d6Gxwl8mYq+f2E^s}Q z>7AG_>?!i0;Gv8x{O%7#Id!^9bd`j$5(;H8H4TfN>bXugO~;q?>E&b$wkG# zDZ5b24>e{Iu?5HDezRVFm1F4<+VuNPRL?%=!Z~}L-tGj zE;A^1RphP;xNA2a4WWLlBv-<7HsiPLk~h2S=|ye?W_G0Ki9ZptyJ*&Izkt@pqqpbc52idFzV|*24Nnf45JrDqaA(sO6hQ>YE-c8LNER zng|UY-shSV8|gTpY{EB)mbn|H6!62%+&zM1FhN+SMjlX^69{ZwpPL)BUd-P{?$FPz zO1K}uLGbSh&C^d( z4(%6*7S~9ocsN?r>jws!mK~mSjmyFM7f!_69Cp2nvnwf=da^VmAsk*b=`p7U$95yn z3VYB7@MyEqQpYrj>dH>?W+*dffinJudCbgCw9E`wa$4L~fFo|gI;IFxJxGMO*AJ$V zH@gez`rRPgQg*723A4+Hp*=xpsVm^I!BIH%9jqB@d)ke=ZfL`VlBsIw`Fv*F5b z>-3wo54qB*yq&_mDPcE8;@-cw_)3QK* z%x4h?a*8@=i&S~4!k0}t+l}>^ysv)dNAV|tmv~Qu@&#^@zI;B`BPHs^JFdZr6Cl07 z-*gsp%-*^OZcD4aI{N8F-0^1h$R3)M_fAcDJQpK^N-Jm*4GZUkZ<8&WhTfcr7qex)$q_4Ul0p|_ah0^W1=)8Eazv?>O+yT zwsPhlI;{1nWHaxs;{p?=U=dboa8!fY&qW&sYAsCHR0sh|cqKQV;7hd_oo2o}_aB9D zC6@4m4*sJz7i$Rn-%7^7DenI9+o)XaSfu2yhX+a z795Rq7&-d9+udCo-8EN*PIWMiH@;BLFi@EYrua2IJJ`|#PU(HYWofLx%UG*fDE{U; z@bOmSyikQ>l18vjgOcV=MUnG8o&pK>??fK9t8NR3os1!BhVk1^*VD?sg-%rH$r^~L zQGJ{d!`dld-_&TBSP(e56<2%6ZEv5xZ3qCv?E5F#uC8^QzZ9GpX@1<}RB^oxJnUR= zgW^2^MB;hfspF9ORxk@a)(_U)m_N*^!noMaY&o@t(EUaaML1%2T#~nIRwu{Xi%?Iln0@oA z3~IKXeZPh0_?H4z=njkMTA#mI7@WZKEUAcOVNw)}rURfbO-3l4&LSw>Am= zJrn3_q`z2-;wNq39cigx*%__FDJYO-$;hS3qXyPKtB<6Fjx24^FK8||KeG0)WFSu!II>ocRh?E)UUsf9 zC&TxcPeTUOWYl$mR!(Q@4c={8-RO1QB*rqXxio4dJ4Tqzty{yA?kfdzy4 zwdte8O~Yhd&<=_y0&2`21t4$o=Fa@+w-xvx#rkWLAv5ppQ|;BD6~pM-u37UywF1Ku z!bPH4LN7hnSa7>;>)Xe^gyNYMkY@!LX~#kD!OK71=;eX zibW>8Kn_$hsJmYVN*co)@6m(f)1=w4oNE(p5o=XLn936`A0!u?J$HcpoYRwX(aR!9 z?fM+u^Rlm-7D9IYnz8glHu26(1@gu~Zk3HbpZjmBG53}z_@tn@Ze(>e8g=hG1-UXW zponOjB#$5-%(hoS7K#0PP7J{I=SDEb>p>JyPRm>G{u)MWcvh-MW?|k$X^mZHKDM}l zgsSw2mGPt!a#~({ONlX(f6DG%uhhkAsQSANv=Tx?t!bUrtQtSI26mS`H~4GOl$9m? zzysr|+~G?yoawNxNTnSq4S4s+VhCXa2m?Mh(8N>g{c<0M!RU|7+MKHf0sPOZ&F(ac z!G6d(B;I9E;Nka2j>_M3%B0c*u#H{|%TZAv3x4*M1pv;?HxeMBgW0TGnI}S$L zBprkM&o+mRt!PW3owEl&on7eus-|zP!9Dfxf7^znAmzz!{;ox`_u>%A{Gom20I1-^7)Vq~}gS&nu zf+99|)|k85%{7eO*0fMuzCy5C;F_(>S5)xAejZ|{&YIcTV}b@u)n&D%0CSq6+V{)U zwX*Ap=eMUN6gv!FL1-@6sIl;(OUGT9%Xe;k(EAfl2qbtvSj^(6$5w&R>GRkY@0dJuHZA)RF<sF$*q?!f_NaCW_EV zsxigKT)d=Q*af8$Lcd42B^+d$uM~6Y?JD?<&5&UoNbH zr!I>av;GoR%O2ohZGBd11h0LvJml&m0aK+7OCRpTZQdUqdzpu*B<)qN8f*jJF4ZiFf>ad}YV3O}a0gW?zBPymSu@B2w4LiUSqj^=x2&FrrV?303ju75o`Hc~Li zv?bOPABvoRa@qGW=-HxC#W2J`!99>v{Jjb&!8rab$eXwkFqKMX1S)$}?jzO8FSDs^ zuQ~bEZ6k9rpM!xvx)zR>Hl(OR8rxQbI>FxS4gnkF7{ddijML-i16QS6`KypEsZk(< zv-;7SaQX{x0JXO$;$3Baf%bY>*t*HR9%#H3CT+LYTm@j{S8p+&X*lP_=xlk5p?D99 zMV;11oGvZ*4o}^r+-X3DtatZNy)sqJcM@JA*xfc?&yp%#&S!At(V!J_yIJ*?;K=1% zn2fbc1Lh$2ILW|$dZB~fvg$imk{j@o_O_1P{seMO|MJ&C(|QK^M_U58kfb=VP~%L( zTnB0F7xT=LOn`?!ZmXZSfj`hNZZW!rgrjIC8{I1)`5zQ;rt2Yk(=Hv1U>Fw2nfe1x zE|v}LS>dP*g+tl%rBbT`@Dw+9rcNPhr+!HR6ELUmmASZA%id~Buu>@~V$AfZc&$@O zIByo+rymY7OAIM#X|(NLpbWOF#cvSHg4;Ez5@=*{#{K^8mpbwJy9q~HryKJe%3O=qN z0FUX?v=8+ly1BTmYv@JetNC1k9A4V$7cPCwHTm@WF?`d6$5r;0p>OWo9t{AY_dTEqxdoDhkW+4goup@t2 zGS4W|AFRTqO>7y61y&Yn6$L%|p@1`uMoehwxg@JS^MMVnY7+LkDAw8YQ??$mwMPH+ z{$TKIHlSj?Iz*od-Kh~9JP!TNV-wa|3=Z`cxw_Z8xdZ8jcr+8S{yqux2mAr6W9xX2`_PrY2HVtwaJlkQIj2AJe z4V@gC2dWhaK|dh608Z0RDr{5m-5^P{F+K0qJtq6G_PCG>JctTv8$8)DUBfsPk<~4n zV!p(L`w&zYnr!BZw#82S4Z->VZf8ZQhE{{^0k%&9lO+EJCqhdnIORB9QCecex%VzS zsFjRA!|J*D7D&ZGje?_Uv<8Um*!DQ|1mDzz1(?S*Ilo|+%W#quvcp_v^_-`{bdA?* zLx1TFQdd4w%wHAtyI0f&l~*_4A;C6HwqxTnI$Bp77QuRyjsr|!= zSn+!;(j~WfIgbiA@*a8D<&v%7Ih@?=Q844Ze6*V}sJ4xLaLZr^UJFif#>Gu0A)jlHJNhmH{Opw3Q?c3fHm|G)ZU-@gJ_0^4zSON?Iw z?TV?wtI;q)xo?qQ4fY=zGdn+6!kntX#cy*qEvd4C5~G;*X4i@FaxxUd0=Xyreo#;UCL1@%Ns)CBYm=jH*QSiZrW4 zjaeoja`!78`aTBFW5AuS^HFl&Y+qS@d1Og+|_IItu&IfJBZGtLW_Q^2Xd&%Izw~ zB8Q@WfhQe=?p_VHwDLCV*qZnT+c|7hGJJTl3wj z@4C4FioI%x*Tus)S105#mA2?Sm(!~vGd-hUz*iyne1aQ({PIx2y@!^Mb;|vpho1Ka zn($<_tgn9t!fVl!uwH38tQN6U*L@dL3G`Q$KNO*Qqa0yBQ9J_0{3*LM@Al%~GwK5W zeRlM@`mIZVI6lTHk$?4P|D19xKydeI^NGY+*Yh*UB+_N7+fa5m5Fa-DGl$f7t9Ux1 zSMd}pR{_AsY}B^X|88@WyUntV;C)aK_PVDv1 zt_j_y{FecLPNeb3`IB2MVgFa0|M|qIp0Fi`V~Y7V1#p8KILlPHDb90Na+^Gt#OHaJtbRbP?cO6_a%t z6wI0%`}A;y9`*yEl5_{yb(|({&<3Jlj~4pUY!EBDag9>|@mVey4f{w37`Gnwfta+P z^~NIj^uW?_-%;D%p#qa$fUeQF`q5oRRRs;oJ_+nW6r>LYIv2Hq&P5>hSo4YjqPGR$ zxTWG7J`*bzd0R$YPst(h9xY3ZG;mTy)_b8RvvX-MC!!?J>*pQ`V)!`}gDalmdhVLf zHy81&9ek>?_syF9hmTis>fel*x&2~(yu!&Mn_$w(2($D6PS^f}g{LWSr+Qw_vFFTI zp9=&w}u`iE-v)ztaZxv*ckK{D2?97G$ajrQ8TPlmsv?^)P;baiH~`g%n71rPs|&->yL z*tSr1ZmiQDYnk*V&B)8Kw=s3zP8HM7R=651zIp+sR&6(r>#k?~2ou0#Yv@0T0wQnQ zCS$W0ToFa@p}rg3n{Bsv@D-e9_~VPO}-#Udtjq1CACs z0%Vw)50!dT6cv}F7Olvu75zOXhN=!!zP=`v!jeFM3w3QC;J$hUaF_D?ixIE7P?Izm zS>Yv*m=}_d_x-W7^Unrr_P<5jo6Tp6IR4>oeqDKYc~}BZC@Fika+EQE1JyM#`+txV z(6pSiGk0y(lZ(kIIGXS&ue5-LBu9W1MWadkWR6*}cF)%jmwH|D(j1$P@+tJ3^=$oJ zsL4%cYd!(&0nmHJ%}Kfij{(egPLcqr-<)~c{O#(6?-poETmV3Y^Th^5KC$20+Y8_w z8V^6$#{gumu*Wg@r~6mme}1OwX7RqC7b1SyKL7pkyxYqWs-Aa^U|9{>mly-{*2xvyaYx!o+V z(k_io^U=(6LdiXBkG;?>a#>~V_x*D-F$Qul(no`QSb-;=wJ<-j;#04VJP%6WV-E8? zR#6juEly-2_gfcIMl5-rK#5WnOFcIH$5Mrm4S;c=O5XMSS6u!1**_8vIU#=gE$CWr z)3VPXeg@1*_{U>M6&KEc3V9|V@+P?yR<}MR&Bo6SnY&|YVRsph9i07lWz+M7W% zOG)+a*Ur|Bmv8`mcbbbjcLeo3_hA68?rg;{pYKTHLvUk7+HI^tRv>8OwLgtC-?c+p z$85vq-rmh02P{!9lR~)1g6(TT5s2ZXJ$v|Q+E>+#>Fj$D;S-Pc?$`6izP+~bVAm_8 zMIE;Zzr`mCeRJr)qq3?T6DT`381}-&S^)Yv3AZyVsVUkZC?5dWyb%grCBsXiXdSWm zPk@)&W&4!R!Bo-me&d1X(~5wr*IeJF`SPTZn-m4a0R{rbYY=Xl04TlvD*)v=2N2=V z(fzKyX=)0zb6Ly7MXA^r!{XDJlKz>=uVI19&0WRXH9lZ@ zpRqfQmZKR@r#=Ymdd6&b1|nCBKtS?^kDQCQV6TUltWVSjCY5z7cCX7wI_}OF zaIWxLLw~r?Qrw_=P{oQ>60My?Kgj>!_lIkZ2OR)0TRXGmr#;(z#OR?id}g=zzjtiN z3{&}+WzebFz+iQDfk<(Z)!RZ#>1%n6B?QGlMUHAwtx@^_%iIY-cdq50ZYv`tb3hHw zCkR3|%D2pbcy$JVJIZh|1a=Gn=sUr-a#*(%c?5*6tyOqxm=VquwBDlD1O;OhwIQ6* zN~`i-fQCJi#u+Ug^@-ttB!j;l!j$%oCtY$+fs6VBxQEvoBxa=!oxjFSKI>@+_J>@23X^#J@n2k0l=)z;_; z+vX=`>2C%urU3Se92M1Hx|p4m2pG^4u7Gr}i76929Fw3DRKun29A|OC3-DrpG_`Vh z1E5~;c^QjRyXVzwO^2GftYF)XN1JRiL|X)p;b|`Mmc9x4l>@@v3DRvfTz4U7eTsl% zOQ+uu)?T+35nJ8u73eJnPSNGotp2^f*hIIkfWr!$9N^nyO_SUz%#OsvfQ)1Vf;;2s zH?fKFDpThMdkVu~61%S4E|K&9K7+XFIc({d0C@ZZPFzC_QW($;Sy7hdNgPfA;1jw4 z;L4>OcIzS@%nPV>9|ixb)%)jR2twU@6qYbjl4*&}K2#tQV}?b<0e2RZe_N+O3vcDf z0Qy_{RBlZKCMgl>@HISAcF#!5)^r4?>4g{}I;v^<=V1?9gzBQRDqF;X>fPsgS8%|? zoVNG)0ig-D&uU#J%Nu<4bbzL)$AAnPy-_m}wM{Lpw^@CAa zD^lp#lVW1y3(y9M0AD|+gx%N7fPKEGFT1}_!%`QXLFA5E)78ZET~{I5)xKC^#O!)U zcP1o*s4a48b`IPrss04yp!3P8drkmGKmTs#G=2^yd`#l!X6j|qF{pR$b^37mF9PFc z!tX#P?!X};(ZI8nnGG&w1J#bU|q~eE# znM*iQH z1NiWs<4+U<`RITonFvg_IR?;3r2<6fFL&(BGj$%=eSd6E_K3Fd>KVlt{e?ryU}}xx zJ5?_;YTyzfUQxVHVtfP`pH3i)@zX-|Uy+-!#Ev&2tz!DPU~2x7TDU&|#EejJr7U!? zM8zT}&waSjaERwC3EsZ3(08^@TrEkjL6t$cJUl*=s`dLN`*zN=>F@g2Qt3Owlg}@h z(lA%Ue!jE+)vFnDhbj15gf(X^9J7MDXQ(b(&>8?fSSj74rX+cfjK1JIz;x?ztuiQu zRPEwfW8Jw!EdZk1@y1q(Dj!wvyxfncuRG^yr(sj`DSIk_do*B{w)wN^Tq-hmdJDEsLHmD3$jOf zYi8uywC-(Mh$8E$IWQUGmW*CFodJrkp={yk$$th-b)6xB*}fc_g60o! zj0B%^RAat@x$vfIjpNzd(V+?D7^??U2bSkvT{&X537Ggr0l;c|Ex8BRX{V;<(rt!v zBSl9Z)Y_dkf;IG5%jDoCO5OG(AKb1V)^zjR6o|;gH8^D+r627e}~TYJ%nx9M%26&BPH>o9cns$YnraJv80!H(fZso|#$J0jod^isU!z zAk!rBa>pLxW*dn9!H<>gn!bBcIW~C?p-71?g^+Q8b8D;lm_a2j!pIGvi7vq(^^sBy4dwB9q^Qc5c4PpnXjq}#0+QZS#ZZUtcM1-lO6`djNVTUU zDK65+;!lfWAu8pa0AcU80cL3ALYO`DpxaJG*jN}+?xxA2H_7xt$evav0r%&ReQBay zl13TB^1gLG=ueK}oq#@~2mN5Y@Ebh+ANM%di}YWG9%Y)(pWs{eBvDRiqL!nLAIQg4 z&`7b?C%)BgM4yV@b;DAaoFs)#wr+$np+QjkdxG14-S$!V0gJHXW znX{ou)ZU@Uxv`Xm`htp4k(Z+FDofb|{%G=ayJXi^ygOxT+1j6|joZm7 zKz$HZ%;{?^$n3d~; zYC!<*=lW<=ehUm#kV;nas~8?#!CGhrw1j?&klQW4W-wTR?@JZlDo zE0MZ4$E0ABjbfIy=pvOzn~mal;2?XWN2+Tw_mXT{Z$B^)9Fme0hX_PCjp% zn^D~F1C6tcTmB6EPf)w6TlT3mzx@qHA#a<8o=pF!6DbD+Q6t`p!a3u!KiPzGrTVI0 zQTXyfQIBm%#~#e;d=U^1e@^3M#;Z< z1RY4GkWEj{$~CsLbL3A{^og03wlHBZ9enl~YoJpT#K3u$tK~%>flD_(N1FuFFeS~O zPdrR;>Y%2T)z@#QqZ8de?tzd_(<`@iUiLBNRbcd%^IX&uSRXy0D-HK&*eZEmN zIpO-gpFP^R4qvC5Lc;A&h~2Wu2%O!HO>F`&!S_ZInc7e8Wn_^LlBj;%MHA75^fYaG zqhGvfmqOR4qXL9Bb`&z_m7k*vX~w10H8*5SBhU$gIejT$=Z@hiy`B{1W;;7&|E;W; z>#Vr0n2J7fA<)=yx6zrc$V(F}cr?f4Uu^-w6VDFhiFNK`0kwW$0W~>tS&F4Y*AHHm znf?HISAGGJ#w&4p$79M_C|wa=`Pnm~p0HA;q5|m5zCu||e882l(SUK+lAYEe#iCASryd$jQna{Gii^4%HuaQ8y$8_lKl2*n!$L;^N%2bPJ{!m%u>6l2bXQuFPez_bi;VRmaeZ&(!3$a_^U&7`Wmej$t# zfc})dZXd`?Wo|4_eM)u-5Uve2NL#IXp(@C$3GN1`rm7zBs?s42<7hr{!Nm_&aCDlS z3vcIe-Vw)A1P=dWd$m6e!zNZ4e>hJ7^Q5BOYO{FMsU;7mP2h{;CCx{a!hAjqqINQu z<7@9foc5c_QeL8R=k6<;xHRS}J!Vonc&v0FP|Q%<1#G@+%R+`%cv(a8HP+%Qc4j8y zQ+2PPqE`J4mHdD?F-hP|BWlNR$Z8u285X3*3@EKuHA@YL=?=HO+K*L8v`drACGd8g z6$su=_XlM(&m92i)Tt0?e`Y{AOK(^N%@97y!523!2Bgm6kmFY^!ZGVR6!K8lr4aJ> zUIZxWPWhubl+W@8LMjmF&%Zn}Fg&_dH8C%Rn12C@MLpMmDTneFy%yS!bzuy>*%WQ` zOFJ=`a*bZ<1mm(w+_{EtRM@qk$L=#F*2Gj@g9ymv=A=5jL$qcl`RAE+kqtfaE;17J z$B!XF<+;*WV95FZr@2UVD7g5s=&r(U01-ETZjJcLYQa`4fFy-P`n6Ju;LMI1K1d|qDcm;v!M5iEd z*=JDi1w%>%8Gq)*z=~_t)RUpn!2>y->iZApUE-BDvFzZkLdX>|e_<4qor>*Y1~+I8 z&(R&TgrqH<;Z(7uR0ERzahw5wzU1yTrTfY5MJ}7}8&N$1of0z@?7k{J(Iy{W zqzj2G`cvCrG}|#XSMkx3 z*RQTbI?5ElsP z^iweX>pd&kC%GAVD6aro<};Q*eGsl7*~2o7J09hGUi=wkuLurQK+UPzRNEb0=Oo1^`ezWLPzmdH;qHCRJr$re^5= z8$?b5ldugF7ihzpkjZF z>;5c8*Lw;vYjPvx?RwJ8dFeB@EtY_VLfwj{zivT2#Q>APxh5=(x?_2OHI%# zFhjx&JL&v~ed8Y9@*7snyQlN4h%D}tFe?-H(s~7?7!DoQ98%x$8TFo7=guPO<>#Zz zXSvxjB~ma}T88tx4R~9#%ku#H*sI1LxG8@}LBcyFOb%&x83RmvvJui;^yHv$Y~IKnp$KBxt&x$R0G5y}Cf)<;=WD0^(?8MP@53*h>#{R( z)Y08J*J2_O(kkP1U(Uz2e(Ds+?Xm#m`=1UTpvZeL!8!Uc?~t#cYrNJ^ec*aJ%gkT zT3>7jHrH#&v{+~-dhJb~bpbPeDG}tjZA&l`<8o?>2018207R46v*HI6{fmg280yB3 ziDRiN4nIIICm5j2gM6^q_OdYY5)w@(erpprRlqBrgZADb`Qx{u+pRI8GA#LUuPv=U72SSu9izj0`{% zugostX;lGJF~QtVdP>sU2+a}&)&;9WOsAER0ltFU{g-nM@N$vLxRu|GpSXMyb&1pZWo|BsisOd+jt&tm2OqTu$Y0xsjFA(+AT zY>?CW%U?o^{`{A687C@Sjn}jO{LTOPZGRLdWi`OLhW=|I{$0)4Ek6C{PlvoX1{grc zSFW=DcLn_4Pm%wAn}4t7|JY4`@6Ep-q5mNve+$yz^8WAf Date: Mon, 26 Feb 2024 16:25:39 -0300 Subject: [PATCH 101/102] docs: Fixes to latest What's New (#2219) ## Description: Couple bugs in the last What's New; fixing before announcing. ## Is this change user facing? NO --------- Co-authored-by: mieubrisse --- docs/docs/whats-new.md | 34 +++++++----------- .../img/whats-new/2024-02-26/bug-reports.png | Bin 13219 -> 9962 bytes 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/docs/docs/whats-new.md b/docs/docs/whats-new.md index f83138341f..50d5faa46f 100644 --- a/docs/docs/whats-new.md +++ b/docs/docs/whats-new.md @@ -2,31 +2,28 @@ title: What's New In Kurtosis sidebar_label: What's New slug: '/whats-new' +toc_max_heading_level: 2 --- - - - - +What's New In Kurtosis +====================== + +For detailed information about the changes in each release, see [the changelog](/changelog). 2024-02-26 -========== +---------- -Enclave Builder UI ------------------- +### Enclave Builder UI You can now build enclaves without writing code using the enclave builder UI: -

- -

+![enclave-builder.png](../static/img/whats-new/2024-02-26/enclave-builder.png) The UI will automatically generate Starlark for you, which can be viewed with the "Preview" button. To enable the enclave builder UI, go to the "About" icon in the bottom-left corner of the Kurtosis app and select "Enable experimental enclave builder interface". You'll then see an "Enclave Builder" button in the Enclave List screen. -Kurtosis Github Action ----------------------- +### Kurtosis Github Action You can now run Kurtosis inside your GH Actions CI using [our prebuilt Action](https://github.com/kurtosis-tech/kurtosis-github-action). @@ -43,23 +40,18 @@ You can now run Kurtosis inside your GH Actions CI using [our prebuilt Action](h This can be useful for integration & end-to-end tests, and can be paired with [ImageBuildSpec](https://docs.kurtosis.com/api-reference/starlark-reference/image-build-spec/) so that Kurtosis will both build the image and instantiate the environment. -Human-friendly plan steps -------------------------- +### Human-friendly plan steps The default way `kurtosis run` describes plan steps is now human-friendly: -

- -

+![human-friendly-descriptions.png](../static/img/whats-new/2024-02-26/human-friendly-descriptions.png) You can return to the previous way by adding `--verbosity brief` to your `kurtosis run` flags. -# Bugs & Feedback +### Bugs & Feedback You can now submit bugs from the Kurtosis app itself using the button in the bottom-left: -

- -

+![bug-reports.png](../static/img/whats-new/2024-02-26/bug-reports.png) We're very interested in your feedback about the above features, so let us know what you think! diff --git a/docs/static/img/whats-new/2024-02-26/bug-reports.png b/docs/static/img/whats-new/2024-02-26/bug-reports.png index e6df0e7ead2a4504b9e1907049dfa26169810533..fa9d066938239c4754fbbac042b30511d8a18d54 100644 GIT binary patch delta 5990 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 UTJ$Or{z7hY!s7T#eR9NK0fz_XjsO4v literal 13219 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 Date: Tue, 27 Feb 2024 00:19:06 -0300 Subject: [PATCH 102/102] docs: Add Github icon to docs header, and swap Release Notes for What's New (#2220) ## Description: Screen Shot 2024-02-26 at 17 37 33 Co-authored-by: mieubrisse --- docs/docusaurus.config.js | 15 ++++++++------- docs/src/css/custom.css | 18 +++++++++++++++++- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index e84c675db0..2840961450 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -88,15 +88,16 @@ const config = { activeBasePath: '/sdk' }, { - href: 'https://www.kurtosis.com/release-notes', + to: '/whats-new', position: 'left', - label: 'Release Notes', - }, - { - href: 'https://github.com/kurtosis-tech/kurtosis/issues/new?assignees=leeederek&labels=docs&template=docs-issue.yml', - position: 'right', - label: 'Report Docs Issue', + label: "What's New", + activeBasePath: '/whats-new' }, + { + href: 'https://github.com/kurtosis-tech/kurtosis/', + position: 'right', + className: 'header-github-link', + } ], }, footer: { diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 3e11ab01c7..3f98e0b55f 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -54,6 +54,22 @@ main { background-color: #111111; } +.header-github-link::before { + content: ''; + width: 24px; + height: 24px; + display: flex; + background-color: var(--ifm-navbar-link-color); + mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E"); + transition: background-color var(--ifm-transition-fast) + var(--ifm-transition-timing-default); +} + +.header-github-link:hover::before { + background-color: var(--ifm-navbar-link-hover-color); +} + + /*Stlying for search bar*/ [data-theme='light'] .DocSearch { /* --docsearch-primary-color: var(--ifm-color-primary); */ @@ -93,4 +109,4 @@ main { var(--ifm-color-emphasis-200) 0%, var(--ifm-color-emphasis-100) 100% ); -} \ No newline at end of file +}
Commits