From 7bad2a9123f1a8a68d0a3935c963d971fd71c9c2 Mon Sep 17 00:00:00 2001 From: leoporoli Date: Fri, 17 May 2024 09:30:15 -0300 Subject: [PATCH] feat: adding the `starlark run` repository to maintain this data after an APIC restart (#2452) ## Description adding the `starlark run` repository to have this data after an APIC restart ## REMINDER: Tag Reviewers, so they get notified to review ## Is this change user facing? NO ## References (if applicable) --------- Co-authored-by: Laurent Luce --- .../src/engine/rest_api_bindings/types.d.ts | 2656 ++++++++++------- core/server/api_container/main.go | 7 + .../server/api_container_service.go | 191 +- .../starlark_run/repository.go | 92 + .../starlark_run/repository_test.go | 60 + .../starlark_run/starlark_run.go | 143 + .../starlark_run/starlark_run_test.go | 51 + 7 files changed, 2084 insertions(+), 1116 deletions(-) create mode 100644 core/server/api_container/server/startosis_engine/starlark_run/repository.go create mode 100644 core/server/api_container/server/startosis_engine/starlark_run/repository_test.go create mode 100644 core/server/api_container/server/startosis_engine/starlark_run/starlark_run.go create mode 100644 core/server/api_container/server/startosis_engine/starlark_run/starlark_run_test.go 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 302f7d258b..4c64ace51c 100644 --- a/api/typescript/src/engine/rest_api_bindings/types.d.ts +++ b/api/typescript/src/engine/rest_api_bindings/types.d.ts @@ -3,1076 +3,1610 @@ * Do not make direct changes to the file. */ - export interface paths { - "/engine/info": { - /** Get engine info */ - get: { - responses: { - /** @description Successful response */ - 200: { - content: { - "application/json": components["schemas"]["EngineInfo"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves": { - /** List enclaves */ - get: { - responses: { - /** @description Successful response */ - 200: { - content: { - "application/json": { - [key: string]: components["schemas"]["EnclaveInfo"]; - }; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - /** Create enclave */ - post: { - requestBody: { - content: { - "application/json": components["schemas"]["CreateEnclave"]; - }; - }; - responses: { - /** @description Successful response */ - 200: { - content: { - "application/json": components["schemas"]["EnclaveInfo"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - /** - * Delete enclaves - * @description Delete stopped enclaves. TO delete all the enclaves use the query parameter `remove_all` - */ - delete: { - parameters: { - query?: { - remove_all?: components["parameters"]["remove_all"]; - }; - }; - responses: { - /** @description Successful response */ - 200: { - content: { - "application/json": components["schemas"]["DeletionSummary"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/history": { - /** List all enclave identifiers */ - get: { - responses: { - /** @description Successful response */ - 200: { - content: { - "application/json": components["schemas"]["EnclaveIdentifiers"][]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}": { - /** Get enclave detailed info */ - get: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - responses: { - /** @description Successful response */ - 200: { - content: { - "application/json": components["schemas"]["EnclaveInfo"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - /** Destroy enclave */ - delete: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - responses: { - /** @description Successful response */ - 200: { - content: never; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/status": { - /** Get enclave status */ - get: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - responses: { - /** @description Successful response */ - 200: { - content: { - "application/json": components["schemas"]["EnclaveStatus"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - /** Set enclave status */ - post: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - requestBody: { - content: { - "application/json": components["schemas"]["EnclaveTargetStatus"]; - }; - }; - responses: { - /** @description Successful response */ - 200: { - content: never; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/starlark": { - /** Get last Starlark run */ - get: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["StarlarkDescription"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/starlark/packages": { - /** - * Uploads a Starlark package - * @description Uploads a Starlark package. This step is required before the package can be executed with RunStarlarkPackage - */ - post: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - requestBody: components["requestBodies"]["fileUploadBody"]; - responses: { - /** @description Success */ - 200: { - content: never; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/starlark/packages/{package_id}": { - /** - * Executes a Starlark package on the user's behalf - * @description The endpoint will trigger the execution and deployment of a Starlark package. By default, it'll - * return an async logs resource using `starlark_execution_uuid` that can be used to retrieve the logs - * via streaming. It's also possible to block the call and wait for the execution to complete using the - * query parameter `retrieve_logs_async`. - */ - post: { - parameters: { - query?: { - retrieve_logs_async?: components["parameters"]["retrieve_logs_async"]; - }; - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - package_id: components["parameters"]["package_id"]; - }; - }; - requestBody: { - content: { - "application/json": components["schemas"]["RunStarlarkPackage"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["StarlarkRunResponse"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/starlark/scripts": { - /** - * Executes a Starlark script on the user's behalf - * @description The endpoint will trigger the execution and deployment of a Starlark file. By default, it'll - * return an async logs resource using `starlark_execution_uuid` that can be used to retrieve the logs - * via streaming. It's also possible to block the call and wait for the execution to complete using the - * query parameter `retrieve_logs_async`. - */ - post: { - parameters: { - query?: { - retrieve_logs_async?: components["parameters"]["retrieve_logs_async"]; - }; - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - requestBody: { - content: { - "application/json": components["schemas"]["RunStarlarkScript"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["StarlarkRunResponse"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/services/{service_identifier}": { - /** Returns detailed information about a specific service */ - get: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - service_identifier: components["parameters"]["service_identifier"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["ServiceInfo"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/services/history": { - /** Returns information about all existing & historical services */ - get: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["ServiceIdentifiers"][]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/services": { - /** Returns detailed information about alls services within the enclave */ - get: { - parameters: { - query?: { - /** @description Select services to get information */ - services?: string[]; - }; - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": { - [key: string]: components["schemas"]["ServiceInfo"]; - }; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/services/{service_identifier}/command": { - /** Executes the given command inside a running service's container */ - post: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - service_identifier: components["parameters"]["service_identifier"]; - }; - }; - /** @description Exec Command */ - requestBody: { - content: { - "application/json": components["schemas"]["ExecCommand"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["ExecCommandResult"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/services/{service_identifier}/endpoints/{port_number}/availability": { - /** - * Check for service availability - * @description Block until the given HTTP endpoint returns available, calling it through a HTTP request - */ - get: { - parameters: { - query?: { - http_method?: components["parameters"]["http_method"]; - path?: components["parameters"]["path"]; - initial_delay_milliseconds?: components["parameters"]["initial_delay_milliseconds"]; - retries?: components["parameters"]["retries"]; - retries_delay_milliseconds?: components["parameters"]["retries_delay_milliseconds"]; - expected_response?: components["parameters"]["expected_response"]; - request_body?: components["parameters"]["request_body"]; - }; - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - service_identifier: components["parameters"]["service_identifier"]; - port_number: components["parameters"]["port_number"]; - }; - }; - responses: { - /** @description Success */ - 200: { - content: never; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/artifacts": { - /** List all files artifacts */ - get: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["FileArtifactReference"][]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/artifacts/{artifact_identifier}": { - /** Inspect the content of a file artifact */ - get: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - artifact_identifier: components["parameters"]["artifact_identifier"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["FileArtifactDescription"][]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/artifacts/{artifact_identifier}/download": { - /** Downloads a files artifact from the Kurtosis File System */ - get: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - artifact_identifier: components["parameters"]["artifact_identifier"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/octet-stream": string; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/artifacts/local-file": { - /** Uploads local file artifact to the Kurtosis File System */ - post: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - requestBody: components["requestBodies"]["fileUploadBody"]; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": { - [key: string]: components["schemas"]["FileArtifactUploadResult"]; - }; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/artifacts/remote-file": { - /** - * Add remote file to Kurtosis File System - * @description Tells the API container to download a files artifact from the web to the Kurtosis File System - */ - post: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - /** @description Store Web Files Artifact */ - requestBody: { - content: { - "application/json": components["schemas"]["StoreWebFilesArtifact"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["FileArtifactReference"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/artifacts/services/{service_identifier}": { - /** - * Add service's file to Kurtosis File System - * @description Tells the API container to copy a files artifact from a service to the Kurtosis File System - */ - post: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - service_identifier: components["parameters"]["service_identifier"]; - }; - }; - requestBody: { - content: { - "application/json": components["schemas"]["StoreFilesArtifactFromService"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["FileArtifactReference"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/services/connection": { - /** User services port forwarding */ - post: { - parameters: { - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - requestBody: { - content: { - "application/json": components["schemas"]["Connect"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: never; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/logs": { - /** - * Get enclave's services logs - * @description Get multiple enclave services logs concurrently. This endpoint can stream the logs by either starting - * a Websocket connection (recommended) or legacy HTTP streaming. - */ - get: { - parameters: { - query: { - service_uuid_set: components["parameters"]["service_uuid_set"]; - follow_logs?: components["parameters"]["follow_logs"]; - conjunctive_filters?: components["parameters"]["conjunctive_filters"]; - return_all_logs?: components["parameters"]["return_all_logs"]; - num_log_lines?: components["parameters"]["num_log_lines"]; - }; - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - }; - }; - responses: { - /** @description Successful response */ - 200: { - content: { - "application/json": components["schemas"]["ServiceLogs"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/enclaves/{enclave_identifier}/services/{service_identifier}/logs": { - /** - * Get service logs - * @description Get service logs. This endpoint can stream the logs by either starting - * a Websocket connection (recommended) or legacy HTTP streaming. - */ - get: { - parameters: { - query?: { - follow_logs?: components["parameters"]["follow_logs"]; - conjunctive_filters?: components["parameters"]["conjunctive_filters"]; - return_all_logs?: components["parameters"]["return_all_logs"]; - num_log_lines?: components["parameters"]["num_log_lines"]; - }; - path: { - enclave_identifier: components["parameters"]["enclave_identifier"]; - service_identifier: components["parameters"]["service_identifier"]; - }; - }; - responses: { - /** @description Successful response */ - 200: { - content: { - "application/json": components["schemas"]["ServiceLogs"]; - }; - }; - default: components["responses"]["NotOk"]; - }; - }; - }; - "/starlark/executions/{starlark_execution_uuid}/logs": { - /** - * Get Starlark execution logs - * @description Stream the logs of an Starlark execution that were initiated using `retrieve_logs_async`. - * The async logs can be consumed only once and expire after consumption or 2 hours after creation. - * This endpoint can stream the logs by either starting a Websocket connection (recommended) or - * legacy HTTP streaming. - */ - get: { - parameters: { - path: { - starlark_execution_uuid: components["parameters"]["starlark_execution_uuid"]; - }; - }; - responses: { - /** @description Successful request */ - 200: { - content: { - "application/json": components["schemas"]["StarlarkRunResponseLine"][]; - }; - }; - default: components["responses"]["NotOk"]; - }; + "/engine/info": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get engine info */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EngineInfo"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List enclaves */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: components["schemas"]["EnclaveInfo"] | undefined; + }; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + /** Create enclave */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateEnclave"]; + }; + }; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EnclaveInfo"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + /** + * Delete enclaves + * @description Delete stopped enclaves. TO delete all the enclaves use the query parameter `remove_all` + */ + delete: { + parameters: { + query?: { + /** @description If true, remove all enclaves. Otherwise only remove stopped enclaves. Default is false */ + remove_all?: components["parameters"]["remove_all"]; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeletionSummary"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/history": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List all enclave identifiers */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EnclaveIdentifiers"][]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get enclave detailed info */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EnclaveInfo"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + /** Destroy enclave */ + delete: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + default: components["responses"]["NotOk"]; + }; + }; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get enclave status */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EnclaveStatus"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + /** Set enclave status */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["EnclaveTargetStatus"]; + }; + }; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + default: components["responses"]["NotOk"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/starlark": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get last Starlark run */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["StarlarkDescription"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/starlark/packages": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Uploads a Starlark package + * @description Uploads a Starlark package. This step is required before the package can be executed with RunStarlarkPackage + */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: components["requestBodies"]["fileUploadBody"]; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + default: components["responses"]["NotOk"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/starlark/packages/{package_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Executes a Starlark package on the user's behalf + * @description The endpoint will trigger the execution and deployment of a Starlark package. By default, it'll + * return an async logs resource using `starlark_execution_uuid` that can be used to retrieve the logs + * via streaming. It's also possible to block the call and wait for the execution to complete using the + * query parameter `retrieve_logs_async`. + */ + post: { + parameters: { + query?: { + /** @description If false, block http response until all logs are available. Default is true */ + retrieve_logs_async?: components["parameters"]["retrieve_logs_async"]; + }; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + /** @description The package identifier that will be executed */ + package_id: components["parameters"]["package_id"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RunStarlarkPackage"]; + }; + }; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["StarlarkRunResponse"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/starlark/scripts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Executes a Starlark script on the user's behalf + * @description The endpoint will trigger the execution and deployment of a Starlark file. By default, it'll + * return an async logs resource using `starlark_execution_uuid` that can be used to retrieve the logs + * via streaming. It's also possible to block the call and wait for the execution to complete using the + * query parameter `retrieve_logs_async`. + */ + post: { + parameters: { + query?: { + /** @description If false, block http response until all logs are available. Default is true */ + retrieve_logs_async?: components["parameters"]["retrieve_logs_async"]; + }; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RunStarlarkScript"]; + }; + }; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["StarlarkRunResponse"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/services/{service_identifier}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Returns detailed information about a specific service */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + /** @description The service identifier of the container that the command should be executed in */ + service_identifier: components["parameters"]["service_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ServiceInfo"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/services/history": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Returns information about all existing & historical services */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ServiceIdentifiers"][]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/services": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Returns detailed information about alls services within the enclave */ + get: { + parameters: { + query?: { + /** @description Select services to get information */ + services?: string[]; + }; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: components["schemas"]["ServiceInfo"] | undefined; + }; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/services/{service_identifier}/command": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Executes the given command inside a running service's container */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + /** @description The service identifier of the container that the command should be executed in */ + service_identifier: components["parameters"]["service_identifier"]; + }; + cookie?: never; + }; + /** @description Exec Command */ + requestBody: { + content: { + "application/json": components["schemas"]["ExecCommand"]; + }; + }; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ExecCommandResult"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/services/{service_identifier}/endpoints/{port_number}/availability": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Check for service availability + * @description Block until the given HTTP endpoint returns available, calling it through a HTTP request + */ + get: { + parameters: { + query?: { + /** @description The HTTP method used to check availability. Default is GET. */ + http_method?: components["parameters"]["http_method"]; + /** @description The path of the service to check. It mustn't start with the first slash. For instance `service/health` */ + path?: components["parameters"]["path"]; + /** @description The number of milliseconds to wait until executing the first HTTP call */ + initial_delay_milliseconds?: components["parameters"]["initial_delay_milliseconds"]; + /** @description Max number of HTTP call attempts that this will execute until giving up and returning an error */ + retries?: components["parameters"]["retries"]; + /** @description Number of milliseconds to wait between retries */ + retries_delay_milliseconds?: components["parameters"]["retries_delay_milliseconds"]; + /** @description If the endpoint returns this value, the service will be marked as available (e.g. Hello World). */ + expected_response?: components["parameters"]["expected_response"]; + /** @description If the http_method is set to POST, this value will be send as the body of the availability request. */ + request_body?: components["parameters"]["request_body"]; + }; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + /** @description The service identifier of the container that the command should be executed in */ + service_identifier: components["parameters"]["service_identifier"]; + /** @description The port number to check availability */ + port_number: components["parameters"]["port_number"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/artifacts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List all files artifacts */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FileArtifactReference"][]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/artifacts/{artifact_identifier}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Inspect the content of a file artifact */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + /** @description The artifact name or uuid */ + artifact_identifier: components["parameters"]["artifact_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FileArtifactDescription"][]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/artifacts/{artifact_identifier}/download": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Downloads a files artifact from the Kurtosis File System */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + /** @description The artifact name or uuid */ + artifact_identifier: components["parameters"]["artifact_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/octet-stream": string; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/artifacts/local-file": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Uploads local file artifact to the Kurtosis File System */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: components["requestBodies"]["fileUploadBody"]; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: components["schemas"]["FileArtifactUploadResult"] | undefined; + }; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/artifacts/remote-file": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Add remote file to Kurtosis File System + * @description Tells the API container to download a files artifact from the web to the Kurtosis File System + */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + /** @description Store Web Files Artifact */ + requestBody: { + content: { + "application/json": components["schemas"]["StoreWebFilesArtifact"]; + }; + }; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FileArtifactReference"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/artifacts/services/{service_identifier}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Add service's file to Kurtosis File System + * @description Tells the API container to copy a files artifact from a service to the Kurtosis File System + */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + /** @description The service identifier of the container that the command should be executed in */ + service_identifier: components["parameters"]["service_identifier"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["StoreFilesArtifactFromService"]; + }; + }; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FileArtifactReference"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/services/connection": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** User services port forwarding */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["Connect"]; + }; + }; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + default: components["responses"]["NotOk"]; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/logs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get enclave's services logs + * @description Get multiple enclave services logs concurrently. This endpoint can stream the logs by either starting + * a Websocket connection (recommended) or legacy HTTP streaming. + */ + get: { + parameters: { + query: { + service_uuid_set: components["parameters"]["service_uuid_set"]; + follow_logs?: components["parameters"]["follow_logs"]; + conjunctive_filters?: components["parameters"]["conjunctive_filters"]; + return_all_logs?: components["parameters"]["return_all_logs"]; + num_log_lines?: components["parameters"]["num_log_lines"]; + }; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ServiceLogs"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enclaves/{enclave_identifier}/services/{service_identifier}/logs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get service logs + * @description Get service logs. This endpoint can stream the logs by either starting + * a Websocket connection (recommended) or legacy HTTP streaming. + */ + get: { + parameters: { + query?: { + follow_logs?: components["parameters"]["follow_logs"]; + conjunctive_filters?: components["parameters"]["conjunctive_filters"]; + return_all_logs?: components["parameters"]["return_all_logs"]; + num_log_lines?: components["parameters"]["num_log_lines"]; + }; + header?: never; + path: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: components["parameters"]["enclave_identifier"]; + /** @description The service identifier of the container that the command should be executed in */ + service_identifier: components["parameters"]["service_identifier"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ServiceLogs"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/starlark/executions/{starlark_execution_uuid}/logs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Starlark execution logs + * @description Stream the logs of an Starlark execution that were initiated using `retrieve_logs_async`. + * The async logs can be consumed only once and expire after consumption or 2 hours after creation. + * This endpoint can stream the logs by either starting a Websocket connection (recommended) or + * legacy HTTP streaming. + */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description The unique identifier to track the execution of a Starlark script or package */ + starlark_execution_uuid: components["parameters"]["starlark_execution_uuid"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful request */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["StarlarkRunResponseLine"][]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - }; } - export type webhooks = Record; - export interface components { - schemas: { - /** @enum {string} */ - ResponseType: "ERROR" | "INFO" | "WARNING"; - ResponseInfo: { - type: components["schemas"]["ResponseType"]; - message: string; - /** Format: uint32 */ - code: number; - }; - EngineInfo: { - engine_version: string; - }; - CreateEnclave: { - enclave_name: string; - api_container_version_tag: string; - /** @description Enclave log level, defaults to INFO */ - 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"; - /** @enum {string} */ - EnclaveStatus: "RUNNING" | "STOPPED" | "EMPTY"; - /** @enum {string} */ - EnclaveTargetStatus: "STOP"; - /** @enum {string} */ - ApiContainerStatus: "RUNNING" | "STOPPED" | "NON_EXISTENT"; - /** - * @default false - * @enum {boolean} - */ - ApiContainerDebugMode: false | true; - EnclaveInfo: { - enclave_uuid: string; - name: string; - shortened_uuid: string; - containers_status: components["schemas"]["EnclaveStatus"]; - api_container_status: components["schemas"]["ApiContainerStatus"]; - api_container_info?: components["schemas"]["EnclaveAPIContainerInfo"]; - api_container_host_machine_info?: components["schemas"]["EnclaveAPIContainerHostMachineInfo"]; - creation_time: components["schemas"]["Timestamp"]; - mode: components["schemas"]["EnclaveMode"]; - }; - EnclaveAPIContainerInfo: { - container_id: string; - ip_inside_enclave: string; - grpc_port_inside_enclave: number; - bridge_ip_address: string; - }; - EnclaveAPIContainerHostMachineInfo: { - ip_on_host_machine: string; - grpc_port_on_host_machine: number; - }; - EnclaveIdentifiers: { - enclave_uuid: string; - name: string; - shortened_uuid: string; - }; - EnclaveNameAndUuid: { - name: string; - uuid: string; - }; - DeletionSummary: { - removed_enclave_name_and_uuids?: components["schemas"]["EnclaveNameAndUuid"][]; - }; - /** Format: date-time */ - Timestamp: string; - /** @description Shared Objects (Used By Multiple Endpoints) */ - Port: { - /** Format: int32 */ - number: number; - transport_protocol: components["schemas"]["TransportProtocol"]; - application_protocol?: string; - /** @description The wait timeout duration in string */ - wait_timeout?: string; - }; - /** @enum {string} */ - HttpMethodAvailability: "GET" | "POST"; - Container: { - status: components["schemas"]["ContainerStatus"]; - image_name: string; - entrypoint_args: string[]; - cmd_args: string[]; - env_vars: { - [key: string]: string; - }; - }; - /** - * @description 0 - STOPPED - * 1 - RUNNING - * 2 - UNKNOWN - * @enum {string} - */ - ServiceStatus: "STOPPED" | "RUNNING" | "UNKNOWN"; - /** - * @description 0 - ALWAYS - * 1 - MISSING - * @enum {string} - */ - ImageDownloadMode: "ALWAYS" | "MISSING"; - ServiceInfo: { - /** @description UUID of the service */ - service_uuid: string; - /** @description The IP address of the service inside the enclave */ - private_ip_addr: string; - private_ports: { - [key: string]: components["schemas"]["Port"]; - }; - /** - * @description Public IP address *outside* the enclave where the service is reachable - * NOTE: Will be empty if the service isn't running, the service didn't define any ports, or the backend doesn't support reporting public service info - */ - public_ip_addr?: string; - public_ports?: { - [key: string]: components["schemas"]["Port"]; - }; - /** @description Name of the service */ - name: string; - /** @description Shortened uuid of the service */ - shortened_uuid: string; - service_status: components["schemas"]["ServiceStatus"]; - container: components["schemas"]["Container"]; - }; - /** - * @description 0 - CONNECT // Best effort port forwarding - * 1 - NO_CONNECT // Port forwarding disabled - * @enum {string} - */ - Connect: "CONNECT" | "NO_CONNECT"; - RunStarlarkScript: { - serialized_script: string; - /** @description Parameters data for the Starlark package main function */ - params?: { - [key: string]: unknown; - }; - /** @description Defaults to false */ - dry_run?: boolean; - /** - * Format: int32 - * @description Defaults to 4 - */ - parallelism?: number; - /** @description The name of the main function, the default value is "run" */ - main_function_name?: string; - experimental_features?: components["schemas"]["KurtosisFeatureFlag"][]; - /** @description Defaults to empty */ - cloud_instance_id?: string; - /** @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 */ - params?: { - [key: string]: unknown; - }; - /** @description Defaults to false */ - dry_run?: boolean; - /** - * Format: int32 - * @description Defaults to 4 - */ - parallelism?: number; - /** - * @description Whether the package should be cloned or not. - * If false, then the package will be pulled from the APIC local package store. If it's a local package then is must - * have been uploaded using UploadStarlarkPackage prior to calling RunStarlarkPackage. - * If true, then the package will be cloned from GitHub before execution starts - */ - clone_package?: boolean; - /** @description The relative main file filepath, the default value is the "main.star" file in the root of a package */ - relative_path_to_main_file?: string; - /** @description The name of the main function, the default value is "run" */ - main_function_name?: string; - experimental_features?: components["schemas"]["KurtosisFeatureFlag"][]; - /** @description Defaults to empty */ - cloud_instance_id?: string; - /** @description Defaults to empty */ - cloud_user_id?: string; - image_download_mode?: components["schemas"]["ImageDownloadMode"]; - /** @description Defaults to false */ - non_blocking_mode?: boolean; - /** @description Defaults to empty */ - github_auth_token?: string; - }; - /** - * @description 0 - NO_INSTRUCTIONS_CACHING - * @enum {string} - */ - KurtosisFeatureFlag: "NO_INSTRUCTIONS_CACHING"; - /** @description Starlark Execution Logs */ - StarlarkRunLogs: components["schemas"]["StarlarkRunResponseLine"][]; - /** @description Starlark Execution Response */ - StarlarkRunResponseLine: components["schemas"]["StarlarkInstruction"] | components["schemas"]["StarlarkError"] | components["schemas"]["StarlarkRunProgress"] | components["schemas"]["StarlarkInstructionResult"] | components["schemas"]["StarlarkRunFinishedEvent"] | components["schemas"]["StarlarkWarning"] | components["schemas"]["StarlarkInfo"]; - StarlarkInfo: { - info: { - instruction: { - info_message: string; - }; - }; - }; - StarlarkWarning: { - warning: { - warning_message: string; - }; - }; - StarlarkInstruction: { - position?: components["schemas"]["StarlarkInstructionPosition"]; - instruction_name: string; - arguments: components["schemas"]["StarlarkInstructionArgument"][]; - executable_instruction: string; - is_skipped: boolean; - }; - StarlarkInstructionResult: { - instruction_result: { - serialized_instruction_result: string; - }; - }; - StarlarkInstructionArgument: { - serialized_arg_value: string; - arg_name?: string; - is_representative: boolean; - }; - StarlarkInstructionPosition: { - filename: string; - /** Format: int32 */ - line: number; - /** Format: int32 */ - column: number; - }; - StarlarkError: { - error: components["schemas"]["StarlarkInterpretationError"] | components["schemas"]["StarlarkValidationError"] | components["schemas"]["StarlarkExecutionError"]; - }; - StarlarkInterpretationError: { - interpretation_error: { - error_message: string; - }; - }; - StarlarkValidationError: { - validation_error: { - error_message: string; - }; - }; - StarlarkExecutionError: { - execution_error: { - error_message: string; - }; - }; - StarlarkRunProgress: { - progress_info: { - current_step_info: string[]; - /** Format: int32 */ - total_steps: number; - /** Format: int32 */ - current_step_number: number; - }; + schemas: { + /** @enum {string} */ + ResponseType: "ERROR" | "INFO" | "WARNING"; + ResponseInfo: { + type: components["schemas"]["ResponseType"]; + message: string; + /** Format: uint32 */ + code: number; + }; + EngineInfo: { + engine_version: string; + }; + CreateEnclave: { + enclave_name: string; + api_container_version_tag: string; + /** @description Enclave log level, defaults to INFO */ + 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"; + /** @enum {string} */ + EnclaveStatus: "RUNNING" | "STOPPED" | "EMPTY"; + /** @enum {string} */ + EnclaveTargetStatus: "STOP"; + /** @enum {string} */ + ApiContainerStatus: "RUNNING" | "STOPPED" | "NON_EXISTENT"; + /** + * @default false + * @enum {boolean} + */ + ApiContainerDebugMode: false | true; + EnclaveInfo: { + enclave_uuid: string; + name: string; + shortened_uuid: string; + containers_status: components["schemas"]["EnclaveStatus"]; + api_container_status: components["schemas"]["ApiContainerStatus"]; + api_container_info?: components["schemas"]["EnclaveAPIContainerInfo"]; + api_container_host_machine_info?: components["schemas"]["EnclaveAPIContainerHostMachineInfo"]; + creation_time: components["schemas"]["Timestamp"]; + mode: components["schemas"]["EnclaveMode"]; + }; + EnclaveAPIContainerInfo: { + container_id: string; + ip_inside_enclave: string; + grpc_port_inside_enclave: number; + bridge_ip_address: string; + }; + EnclaveAPIContainerHostMachineInfo: { + ip_on_host_machine: string; + grpc_port_on_host_machine: number; + }; + EnclaveIdentifiers: { + enclave_uuid: string; + name: string; + shortened_uuid: string; + }; + EnclaveNameAndUuid: { + name: string; + uuid: string; + }; + DeletionSummary: { + removed_enclave_name_and_uuids?: components["schemas"]["EnclaveNameAndUuid"][]; + }; + /** Format: date-time */ + Timestamp: string; + /** @description Shared Objects (Used By Multiple Endpoints) */ + Port: { + /** Format: int32 */ + number: number; + transport_protocol: components["schemas"]["TransportProtocol"]; + application_protocol?: string; + /** @description The wait timeout duration in string */ + wait_timeout?: string; + }; + /** @enum {string} */ + HttpMethodAvailability: "GET" | "POST"; + Container: { + status: components["schemas"]["ContainerStatus"]; + image_name: string; + entrypoint_args: string[]; + cmd_args: string[]; + env_vars: { + [key: string]: string | undefined; + }; + }; + /** + * @description 0 - STOPPED + * 1 - RUNNING + * 2 - UNKNOWN + * @enum {string} + */ + ServiceStatus: "STOPPED" | "RUNNING" | "UNKNOWN"; + /** + * @description 0 - ALWAYS + * 1 - MISSING + * @enum {string} + */ + ImageDownloadMode: "ALWAYS" | "MISSING"; + ServiceInfo: { + /** @description UUID of the service */ + service_uuid: string; + /** @description The IP address of the service inside the enclave */ + private_ip_addr: string; + private_ports: { + [key: string]: components["schemas"]["Port"] | undefined; + }; + /** @description Public IP address *outside* the enclave where the service is reachable + * NOTE: Will be empty if the service isn't running, the service didn't define any ports, or the backend doesn't support reporting public service info */ + public_ip_addr?: string; + public_ports?: { + [key: string]: components["schemas"]["Port"] | undefined; + }; + /** @description Name of the service */ + name: string; + /** @description Shortened uuid of the service */ + shortened_uuid: string; + service_status: components["schemas"]["ServiceStatus"]; + container: components["schemas"]["Container"]; + }; + /** + * @description 0 - CONNECT // Best effort port forwarding + * 1 - NO_CONNECT // Port forwarding disabled + * @enum {string} + */ + Connect: "CONNECT" | "NO_CONNECT"; + RunStarlarkScript: { + serialized_script: string; + /** @description Parameters data for the Starlark package main function */ + params?: { + [key: string]: unknown; + }; + /** @description Defaults to false */ + dry_run?: boolean; + /** + * Format: int32 + * @description Defaults to 4 + */ + parallelism?: number; + /** @description The name of the main function, the default value is "run" */ + main_function_name?: string; + experimental_features?: components["schemas"]["KurtosisFeatureFlag"][]; + /** @description Defaults to empty */ + cloud_instance_id?: string; + /** @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 */ + params?: { + [key: string]: unknown; + }; + /** @description Defaults to false */ + dry_run?: boolean; + /** + * Format: int32 + * @description Defaults to 4 + */ + parallelism?: number; + /** @description Whether the package should be cloned or not. + * If false, then the package will be pulled from the APIC local package store. If it's a local package then is must + * have been uploaded using UploadStarlarkPackage prior to calling RunStarlarkPackage. + * If true, then the package will be cloned from GitHub before execution starts */ + clone_package?: boolean; + /** @description The relative main file filepath, the default value is the "main.star" file in the root of a package */ + relative_path_to_main_file?: string; + /** @description The name of the main function, the default value is "run" */ + main_function_name?: string; + experimental_features?: components["schemas"]["KurtosisFeatureFlag"][]; + /** @description Defaults to empty */ + cloud_instance_id?: string; + /** @description Defaults to empty */ + cloud_user_id?: string; + image_download_mode?: components["schemas"]["ImageDownloadMode"]; + /** @description Defaults to false */ + non_blocking_mode?: boolean; + /** @description Defaults to empty */ + github_auth_token?: string; + }; + /** + * @description 0 - NO_INSTRUCTIONS_CACHING + * @enum {string} + */ + KurtosisFeatureFlag: "NO_INSTRUCTIONS_CACHING"; + /** @description Starlark Execution Logs */ + StarlarkRunLogs: components["schemas"]["StarlarkRunResponseLine"][]; + /** @description Starlark Execution Response */ + StarlarkRunResponseLine: components["schemas"]["StarlarkInstruction"] | components["schemas"]["StarlarkError"] | components["schemas"]["StarlarkRunProgress"] | components["schemas"]["StarlarkInstructionResult"] | components["schemas"]["StarlarkRunFinishedEvent"] | components["schemas"]["StarlarkWarning"] | components["schemas"]["StarlarkInfo"]; + StarlarkInfo: { + info: { + instruction: { + info_message: string; + }; + }; + }; + StarlarkWarning: { + warning: { + warning_message: string; + }; + }; + StarlarkInstruction: { + position?: components["schemas"]["StarlarkInstructionPosition"]; + instruction_name: string; + arguments: components["schemas"]["StarlarkInstructionArgument"][]; + executable_instruction: string; + is_skipped: boolean; + }; + StarlarkInstructionResult: { + instruction_result: { + serialized_instruction_result: string; + }; + }; + StarlarkInstructionArgument: { + serialized_arg_value: string; + arg_name?: string; + is_representative: boolean; + }; + StarlarkInstructionPosition: { + filename: string; + /** Format: int32 */ + line: number; + /** Format: int32 */ + column: number; + }; + StarlarkError: { + error: components["schemas"]["StarlarkInterpretationError"] | components["schemas"]["StarlarkValidationError"] | components["schemas"]["StarlarkExecutionError"]; + }; + StarlarkInterpretationError: { + interpretation_error: { + error_message: string; + }; + }; + StarlarkValidationError: { + validation_error: { + error_message: string; + }; + }; + StarlarkExecutionError: { + execution_error: { + error_message: string; + }; + }; + StarlarkRunProgress: { + progress_info: { + current_step_info: string[]; + /** Format: int32 */ + total_steps: number; + /** Format: int32 */ + current_step_number: number; + }; + }; + StarlarkRunFinishedEvent: { + run_finished_event: { + is_run_successful: boolean; + serialized_output?: string; + }; + }; + /** @description Use it to asynchronously retrieve the execution logs via Websockets or http streaming */ + AsyncStarlarkExecutionLogs: { + /** @description Execution UUID to asynchronously retrieve the execution logs */ + async_starlark_execution_logs: { + starlark_execution_uuid: string; + }; + }; + StarlarkRunResponse: { + starlark_execution_logs?: components["schemas"]["AsyncStarlarkExecutionLogs"] | components["schemas"]["StarlarkRunLogs"]; + }; + /** @description An service identifier is a collection of uuid, name and shortened uuid */ + ServiceIdentifiers: { + /** @description UUID of the service */ + service_uuid: string; + /** @description Name of the service */ + name: string; + /** @description The shortened uuid of the service */ + shortened_uuid: string; + }; + /** @description Exec Command */ + ExecCommand: { + command_args: string[]; + }; + ExecCommandResult: { + /** Format: int32 */ + exit_code: number; + /** @description Assumes UTF-8 encoding */ + log_output: string; + }; + FileArtifactUploadResult: { + file_artifact_upload_result?: components["schemas"]["FileArtifactReference"] | components["schemas"]["ResponseInfo"]; + }; + /** @description Files Artifact identifier */ + FileArtifactReference: { + /** @description UUID of the files artifact, for use when referencing it in the future */ + uuid: string; + /** @description UUID of the files artifact, for use when referencing it in the future */ + name: string; + }; + /** @description Store Web Files Artifact */ + StoreWebFilesArtifact: { + /** @description URL to download the artifact from */ + url: string; + /** @description The name of the files artifact */ + name: string; + }; + StoreFilesArtifactFromService: { + /** @description The absolute source path where the source files will be copied from */ + source_path: string; + /** @description The name of the files artifact */ + name: string; + }; + FileArtifactDescription: { + /** @description Path relative to the file artifact */ + path: string; + /** + * Format: int64 + * @description Size of the file, in bytes + */ + size: number; + /** @description A bit of text content, if the file allows (similar to UNIX's 'head') */ + text_preview?: string; + }; + /** + * @description 0 - NEVER + * 1 - ALWAYS + * @enum {string} + */ + RestartPolicy: "NEVER" | "ALWAYS"; + StarlarkDescription: { + package_id: string; + serialized_script: string; + serialized_params: string; + /** Format: int32 */ + parallelism: number; + relative_path_to_main_file: string; + main_function_name: string; + experimental_features: components["schemas"]["KurtosisFeatureFlag"][]; + restart_policy: components["schemas"]["RestartPolicy"]; + }; + /** + * @description 0 - TCP + * 1 - SCTP + * 2 - UDP + * @enum {string} + */ + TransportProtocol: "TCP" | "SCTP" | "UDP"; + /** + * @description 0 - STOPPED + * 1 - RUNNING + * 2 - UNKNOWN + * @enum {string} + */ + ContainerStatus: "STOPPED" | "RUNNING" | "UNKNOWN"; + ServiceLogs: { + service_logs_by_service_uuid?: { + [key: string]: components["schemas"]["LogLine"] | undefined; + }; + not_found_service_uuid_set?: string[]; + }; + LogLine: { + line: string[]; + timestamp: components["schemas"]["Timestamp"]; + }; + LogLineFilter: { + operator: components["schemas"]["LogLineOperator"]; + text_pattern: string; + }; + /** @enum {string} */ + LogLineOperator: "DOES_CONTAIN_TEXT" | "DOES_NOT_CONTAIN_TEXT" | "DOES_CONTAIN_MATCH_REGEX" | "DOES_NOT_CONTAIN_MATCH_REGEX"; }; - StarlarkRunFinishedEvent: { - run_finished_event: { - is_run_successful: boolean; - serialized_output?: string; - }; + responses: { + /** @description Unexpected error */ + NotOk: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ResponseInfo"]; + }; + }; }; - /** @description Use it to asynchronously retrieve the execution logs via Websockets or http streaming */ - AsyncStarlarkExecutionLogs: { - /** @description Execution UUID to asynchronously retrieve the execution logs */ - async_starlark_execution_logs: { + parameters: { + /** @description UUID, shortened UUID, or name of the enclave */ + enclave_identifier: string; + /** @description The service identifier of the container that the command should be executed in */ + service_identifier: string; + /** @description The unique identifier to track the execution of a Starlark script or package */ starlark_execution_uuid: string; - }; - }; - StarlarkRunResponse: { - starlark_execution_logs?: components["schemas"]["AsyncStarlarkExecutionLogs"] | components["schemas"]["StarlarkRunLogs"]; - }; - /** @description An service identifier is a collection of uuid, name and shortened uuid */ - ServiceIdentifiers: { - /** @description UUID of the service */ - service_uuid: string; - /** @description Name of the service */ - name: string; - /** @description The shortened uuid of the service */ - shortened_uuid: string; - }; - /** @description Exec Command */ - ExecCommand: { - command_args: string[]; - }; - ExecCommandResult: { - /** Format: int32 */ - exit_code: number; - /** @description Assumes UTF-8 encoding */ - log_output: string; - }; - FileArtifactUploadResult: { - file_artifact_upload_result?: components["schemas"]["FileArtifactReference"] | components["schemas"]["ResponseInfo"]; - }; - /** @description Files Artifact identifier */ - FileArtifactReference: { - /** @description UUID of the files artifact, for use when referencing it in the future */ - uuid: string; - /** @description UUID of the files artifact, for use when referencing it in the future */ - name: string; - }; - /** @description Store Web Files Artifact */ - StoreWebFilesArtifact: { - /** @description URL to download the artifact from */ - url: string; - /** @description The name of the files artifact */ - name: string; - }; - StoreFilesArtifactFromService: { - /** @description The absolute source path where the source files will be copied from */ - source_path: string; - /** @description The name of the files artifact */ - name: string; - }; - FileArtifactDescription: { - /** @description Path relative to the file artifact */ - path: string; - /** - * Format: int64 - * @description Size of the file, in bytes - */ - size: number; - /** @description A bit of text content, if the file allows (similar to UNIX's 'head') */ - text_preview?: string; - }; - /** - * @description 0 - NEVER - * 1 - ALWAYS - * @enum {string} - */ - RestartPolicy: "NEVER" | "ALWAYS"; - StarlarkDescription: { - package_id: string; - serialized_script: string; - serialized_params: string; - /** Format: int32 */ - parallelism: number; - relative_path_to_main_file: string; - main_function_name: string; - experimental_features: components["schemas"]["KurtosisFeatureFlag"][]; - restart_policy: components["schemas"]["RestartPolicy"]; - }; - /** - * @description 0 - TCP - * 1 - SCTP - * 2 - UDP - * @enum {string} - */ - TransportProtocol: "TCP" | "SCTP" | "UDP"; - /** - * @description 0 - STOPPED - * 1 - RUNNING - * 2 - UNKNOWN - * @enum {string} - */ - ContainerStatus: "STOPPED" | "RUNNING" | "UNKNOWN"; - ServiceLogs: { - service_logs_by_service_uuid?: { - [key: string]: components["schemas"]["LogLine"]; - }; - not_found_service_uuid_set?: string[]; - }; - LogLine: { - line: string[]; - timestamp: components["schemas"]["Timestamp"]; - }; - LogLineFilter: { - operator: components["schemas"]["LogLineOperator"]; - text_pattern: string; - }; - /** @enum {string} */ - LogLineOperator: "DOES_CONTAIN_TEXT" | "DOES_NOT_CONTAIN_TEXT" | "DOES_CONTAIN_MATCH_REGEX" | "DOES_NOT_CONTAIN_MATCH_REGEX"; - }; - responses: { - /** @description Unexpected error */ - NotOk: { - content: { - "application/json": components["schemas"]["ResponseInfo"]; - }; - }; - }; - parameters: { - /** @description UUID, shortened UUID, or name of the enclave */ - enclave_identifier: string; - /** @description The service identifier of the container that the command should be executed in */ - service_identifier: string; - /** @description The unique identifier to track the execution of a Starlark script or package */ - starlark_execution_uuid: string; - service_uuid_set: string[]; - /** @description If false, block http response until all logs are available. Default is true */ - retrieve_logs_async?: boolean; - /** @description If true, remove all enclaves. Otherwise only remove stopped enclaves. Default is false */ - remove_all?: boolean; - /** @description The port number to check availability */ - port_number: number; - /** @description The HTTP method used to check availability. Default is GET. */ - http_method?: components["schemas"]["HttpMethodAvailability"]; - /** @description The path of the service to check. It mustn't start with the first slash. For instance `service/health` */ - path?: string; - /** @description The number of milliseconds to wait until executing the first HTTP call */ - initial_delay_milliseconds?: number; - /** @description Max number of HTTP call attempts that this will execute until giving up and returning an error */ - retries?: number; - /** @description Number of milliseconds to wait between retries */ - retries_delay_milliseconds?: number; - /** @description If the endpoint returns this value, the service will be marked as available (e.g. Hello World). */ - expected_response?: string; - /** @description If the http_method is set to POST, this value will be send as the body of the availability request. */ - request_body?: string; - /** @description The artifact name or uuid */ - artifact_identifier: string; - /** @description The package identifier that will be executed */ - package_id: string; - follow_logs?: boolean; - conjunctive_filters?: components["schemas"]["LogLineFilter"][]; - return_all_logs?: boolean; - num_log_lines?: number; - }; - requestBodies: { - fileUploadBody?: { - content: { - "multipart/form-data": string; - }; + service_uuid_set: string[]; + /** @description If false, block http response until all logs are available. Default is true */ + retrieve_logs_async: boolean; + /** @description If true, remove all enclaves. Otherwise only remove stopped enclaves. Default is false */ + remove_all: boolean; + /** @description The port number to check availability */ + port_number: number; + /** @description The HTTP method used to check availability. Default is GET. */ + http_method: components["schemas"]["HttpMethodAvailability"]; + /** @description The path of the service to check. It mustn't start with the first slash. For instance `service/health` */ + path: string; + /** @description The number of milliseconds to wait until executing the first HTTP call */ + initial_delay_milliseconds: number; + /** @description Max number of HTTP call attempts that this will execute until giving up and returning an error */ + retries: number; + /** @description Number of milliseconds to wait between retries */ + retries_delay_milliseconds: number; + /** @description If the endpoint returns this value, the service will be marked as available (e.g. Hello World). */ + expected_response: string; + /** @description If the http_method is set to POST, this value will be send as the body of the availability request. */ + request_body: string; + /** @description The artifact name or uuid */ + artifact_identifier: string; + /** @description The package identifier that will be executed */ + package_id: string; + follow_logs: boolean; + conjunctive_filters: components["schemas"]["LogLineFilter"][]; + return_all_logs: boolean; + num_log_lines: number; + }; + requestBodies: { + fileUploadBody: { + content: { + "multipart/form-data": string; + }; + }; }; - }; - headers: never; - pathItems: never; + headers: never; + pathItems: never; } - export type $defs = Record; - -export type external = Record; - export type operations = Record; diff --git a/core/server/api_container/main.go b/core/server/api_container/main.go index 2d22cd0624..80332bbbd2 100644 --- a/core/server/api_container/main.go +++ b/core/server/api_container/main.go @@ -9,6 +9,7 @@ import ( "context" "fmt" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/interpretation_time_value_store" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/starlark_run" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider" "net" "os" @@ -226,6 +227,11 @@ func runMain() error { startosis_engine.NewStartosisValidator(&kurtosisBackend, serviceNetwork, filesArtifactStore), startosis_engine.NewStartosisExecutor(starlarkValueSerde, runtimeValueStore, enclavePlan, enclaveDb)) + starlarkRunRepository, err := starlark_run.GetOrCreateNewStarlarkRunRepository(enclaveDb) + if err != nil { + return stacktrace.Propagate(err, "An error occurred creating the starlark run repository") + } + //Creation of ApiContainerService restartPolicy := kurtosis_core_rpc_api_bindings.RestartPolicy_NEVER if serverArgs.IsProductionEnclave { @@ -240,6 +246,7 @@ func runMain() error { restartPolicy, metricsClient, githubAuthProvider, + starlarkRunRepository, ) if err != nil { return stacktrace.Propagate(err, "An error occurred creating the API container service") diff --git a/core/server/api_container/server/api_container_service.go b/core/server/api_container/server/api_container_service.go index 78f6395a3f..eabdf4a60d 100644 --- a/core/server/api_container/server/api_container_service.go +++ b/core/server/api_container/server/api_container_service.go @@ -17,6 +17,7 @@ import ( "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/enclave_structure" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/instructions_plan/resolver" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/plan_yaml" + "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/starlark_run" "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider" "io" "math" @@ -75,7 +76,6 @@ const ( isNotScript = false isNotRemote = false defaultParallelism = 4 - emptySerializedParams = "" ) // Guaranteed (by a unit test) to be a 1:1 mapping between API port protos and port spec protos @@ -96,9 +96,7 @@ type ApiContainerService struct { packageContentProvider startosis_packages.PackageContentProvider - restartPolicy kurtosis_core_rpc_api_bindings.RestartPolicy - - starlarkRun *kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse + starlarkRunRepository *starlark_run.StarlarkRunRepository metricsClient metrics_client.MetricsClient @@ -114,30 +112,22 @@ func NewApiContainerService( restartPolicy kurtosis_core_rpc_api_bindings.RestartPolicy, metricsClient metrics_client.MetricsClient, githubAuthProvider *git_package_content_provider.GitHubPackageAuthProvider, + starlarkRunRepository *starlark_run.StarlarkRunRepository, ) (*ApiContainerService, error) { - var emptyInitialSerializedParams string - emptyInitialSerializedParamsPtr := &emptyInitialSerializedParams - *emptyInitialSerializedParamsPtr = "" + + if err := initStarlarkRun(starlarkRunRepository, restartPolicy); err != nil { + return nil, stacktrace.Propagate(err, "An error occurred initializing the starlark run") + } + service := &ApiContainerService{ filesArtifactStore: filesArtifactStore, serviceNetwork: serviceNetwork, startosisRunner: startosisRunner, startosisInterpreter: startosisInterpreter, packageContentProvider: startosisModuleContentProvider, - restartPolicy: restartPolicy, - starlarkRun: &kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse{ - PackageId: startosis_constants.PackageIdPlaceholderForStandaloneScript, - SerializedScript: "", - SerializedParams: "", - Parallelism: defaultParallelism, - RelativePathToMainFile: startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, - MainFunctionName: "", - ExperimentalFeatures: []kurtosis_core_rpc_api_bindings.KurtosisFeatureFlag{}, - RestartPolicy: kurtosis_core_rpc_api_bindings.RestartPolicy_NEVER, - InitialSerializedParams: emptyInitialSerializedParamsPtr, - }, - metricsClient: metricsClient, - githubAuthProvider: githubAuthProvider, + starlarkRunRepository: starlarkRunRepository, + metricsClient: metricsClient, + githubAuthProvider: githubAuthProvider, } return service, nil @@ -146,7 +136,7 @@ func NewApiContainerService( func (apicService *ApiContainerService) RunStarlarkScript(args *kurtosis_core_rpc_api_bindings.RunStarlarkScriptArgs, stream kurtosis_core_rpc_api_bindings.ApiContainerService_RunStarlarkScriptServer) error { serializedStarlarkScript := args.GetSerializedScript() serializedParams := args.GetSerializedParams() - parallelism := int(args.GetParallelism()) + parallelism := args.GetParallelism() if parallelism == 0 { parallelism = defaultParallelism } @@ -164,7 +154,7 @@ func (apicService *ApiContainerService) RunStarlarkScript(args *kurtosis_core_rp noPackageReplaceOptions := map[string]string{} apicService.runStarlark( - parallelism, + int(parallelism), dryRun, startosis_constants.PackageIdPlaceholderForStandaloneScript, noPackageReplaceOptions, @@ -174,22 +164,58 @@ func (apicService *ApiContainerService) RunStarlarkScript(args *kurtosis_core_rp serializedParams, downloadMode, nonBlockingMode, - args.GetExperimentalFeatures(), - stream) + experimentalFeatures, + stream, + ) + + return nil +} + +func (apicService *ApiContainerService) saveStarlarkRun( + packageId string, + serializedParams string, + serializedStarlarkScript string, + parallelism int, + mainFuncName string, + experimentalFeatures []kurtosis_core_rpc_api_bindings.KurtosisFeatureFlag, +) error { - if apicService.starlarkRun.InitialSerializedParams == nil || *apicService.starlarkRun.InitialSerializedParams == "" { - apicService.starlarkRun.InitialSerializedParams = &serializedParams + var ( + initialSerializedParams string + restartPolicy = int32(kurtosis_core_rpc_api_bindings.RestartPolicy_NEVER.Number()) + ) + + previousStarlarkRun, err := apicService.starlarkRunRepository.Get() + if err != nil { + return stacktrace.Propagate(err, "An error occurred getting the starlark run object from the repository") + } + + if previousStarlarkRun != nil { + restartPolicy = previousStarlarkRun.GetRestartPolicy() + if previousStarlarkRun.GetInitialSerializedParams() == "" { + initialSerializedParams = serializedParams + } } - apicService.starlarkRun = &kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse{ - PackageId: apicService.starlarkRun.PackageId, - SerializedScript: serializedStarlarkScript, - SerializedParams: serializedParams, - Parallelism: int32(parallelism), - RelativePathToMainFile: startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, - MainFunctionName: mainFuncName, - ExperimentalFeatures: experimentalFeatures, - RestartPolicy: apicService.restartPolicy, - InitialSerializedParams: apicService.starlarkRun.InitialSerializedParams, + + experimentalFeaturesSlice := []int32{} + for _, feature := range experimentalFeatures { + experimentalFeaturesSlice = append(experimentalFeaturesSlice, int32(feature.Number())) + } + + currentStarlarkRun := starlark_run.NewStarlarkRun( + packageId, + serializedStarlarkScript, + serializedParams, + int32(parallelism), + startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, + mainFuncName, + experimentalFeaturesSlice, + restartPolicy, + initialSerializedParams, + ) + + if err := apicService.starlarkRunRepository.Save(currentStarlarkRun); err != nil { + return stacktrace.Propagate(err, "An error occurred saving the current starlark run in the repository") } return nil @@ -261,13 +287,14 @@ func (apicService *ApiContainerService) InspectFilesArtifactContents(_ context.C } func (apicService *ApiContainerService) RunStarlarkPackage(args *kurtosis_core_rpc_api_bindings.RunStarlarkPackageArgs, stream kurtosis_core_rpc_api_bindings.ApiContainerService_RunStarlarkPackageServer) error { + var scriptWithRunFunction string var interpretationError *startosis_errors.InterpretationError var isRemote bool var detectedPackageId string var detectedPackageReplaceOptions map[string]string packageIdFromArgs := args.GetPackageId() - parallelism := int(args.GetParallelism()) + parallelism := args.GetParallelism() if parallelism == 0 { parallelism = defaultParallelism } @@ -322,22 +349,8 @@ func (apicService *ApiContainerService) RunStarlarkPackage(args *kurtosis_core_r actualRelativePathToMainFile, scriptWithRunFunction, serializedParams) - apicService.runStarlark(parallelism, dryRun, detectedPackageId, detectedPackageReplaceOptions, mainFuncName, actualRelativePathToMainFile, scriptWithRunFunction, serializedParams, downloadMode, nonBlockingMode, args.ExperimentalFeatures, stream) - - if apicService.starlarkRun.InitialSerializedParams == nil || *apicService.starlarkRun.InitialSerializedParams == "" { - apicService.starlarkRun.InitialSerializedParams = &serializedParams - } - apicService.starlarkRun = &kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse{ - PackageId: packageIdFromArgs, - SerializedScript: scriptWithRunFunction, - SerializedParams: serializedParams, - Parallelism: int32(parallelism), - RelativePathToMainFile: requestedRelativePathToMainFile, - MainFunctionName: mainFuncName, - ExperimentalFeatures: args.ExperimentalFeatures, - RestartPolicy: apicService.restartPolicy, - InitialSerializedParams: apicService.starlarkRun.InitialSerializedParams, - } + apicService.runStarlark(int(parallelism), dryRun, detectedPackageId, detectedPackageReplaceOptions, mainFuncName, actualRelativePathToMainFile, scriptWithRunFunction, serializedParams, downloadMode, nonBlockingMode, args.ExperimentalFeatures, stream) + return nil } @@ -617,7 +630,32 @@ func (apicService *ApiContainerService) ListFilesArtifactNamesAndUuids(_ context } func (apicService *ApiContainerService) GetStarlarkRun(_ context.Context, _ *emptypb.Empty) (*kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse, error) { - return apicService.starlarkRun, nil + + currentStarlarkRun, err := apicService.starlarkRunRepository.Get() + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred getting the starlark run from the repository") + } + + kurtosisFeatureFlags := []kurtosis_core_rpc_api_bindings.KurtosisFeatureFlag{} + for _, experimentalFeature := range currentStarlarkRun.GetExperimentalFeatures() { + kurtosisFeatureFlags = append(kurtosisFeatureFlags, kurtosis_core_rpc_api_bindings.KurtosisFeatureFlag(experimentalFeature)) + } + restartPolicy := kurtosis_core_rpc_api_bindings.RestartPolicy(currentStarlarkRun.GetRestartPolicy()) + initialSerializedParams := currentStarlarkRun.GetInitialSerializedParams() + + getStarlarkRunResponse := &kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse{ + PackageId: currentStarlarkRun.GetPackageId(), + SerializedScript: currentStarlarkRun.GetSerializedScript(), + SerializedParams: currentStarlarkRun.GetSerializedParams(), + Parallelism: currentStarlarkRun.GetParallelism(), + RelativePathToMainFile: currentStarlarkRun.GetRelativePathToMainFile(), + MainFunctionName: currentStarlarkRun.GetMainFunctionName(), + ExperimentalFeatures: kurtosisFeatureFlags, + RestartPolicy: restartPolicy, + InitialSerializedParams: &initialSerializedParams, + } + + return getStarlarkRunResponse, nil } func (apicService *ApiContainerService) GetStarlarkPackagePlanYaml(ctx context.Context, args *kurtosis_core_rpc_api_bindings.StarlarkPackagePlanYamlArgs) (*kurtosis_core_rpc_api_bindings.PlanYaml, error) { @@ -963,6 +1001,18 @@ func (apicService *ApiContainerService) runStarlark( } if runFinishedEvent := responseLine.GetRunFinishedEvent(); runFinishedEvent != nil { + + if err := apicService.saveStarlarkRun( + packageId, + serializedParams, + serializedStarlark, + parallelism, + mainFunctionName, + experimentalFeatures, + ); err != nil { + logrus.Errorf("The Starlark code was successfully executed but something failed when trying to save the 'run' info in the enclave's database. Error was: \n%v", err.Error()) + } + isSuccessful := runFinishedEvent.GetIsRunSuccessful() numberOfServicesAfterRunFinished := 0 if serviceNames, err := apicService.serviceNetwork.GetServiceNames(); err != nil { @@ -1146,3 +1196,34 @@ func convertFromImageDownloadModeAPI(api_mode kurtosis_core_rpc_api_bindings.Ima panic(stacktrace.NewError("Failed to convert image download mode %v", api_mode)) } } + +func initStarlarkRun( + starlarkRunRepository *starlark_run.StarlarkRunRepository, + restartPolicy kurtosis_core_rpc_api_bindings.RestartPolicy, +) error { + + currentStarlarkRun, err := starlarkRunRepository.Get() + if err != nil { + return stacktrace.Propagate(err, "An error occurred getting the current starlark run") + } + + if currentStarlarkRun == nil { + initialStarlarkRun := starlark_run.NewStarlarkRun( + startosis_constants.PackageIdPlaceholderForStandaloneScript, + "", + "", + defaultParallelism, + startosis_constants.PlaceHolderMainFileForPlaceStandAloneScript, + "", + []int32{}, + int32(restartPolicy.Number()), + "", + ) + + if err = starlarkRunRepository.Save(initialStarlarkRun); err != nil { + return stacktrace.Propagate(err, "An error occurred saving the initial starlark run") + } + } + + return nil +} diff --git a/core/server/api_container/server/startosis_engine/starlark_run/repository.go b/core/server/api_container/server/startosis_engine/starlark_run/repository.go new file mode 100644 index 0000000000..4762d03d92 --- /dev/null +++ b/core/server/api_container/server/startosis_engine/starlark_run/repository.go @@ -0,0 +1,92 @@ +package starlark_run + +import ( + "encoding/json" + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/database_accessors/enclave_db" + "github.com/kurtosis-tech/stacktrace" + "github.com/sirupsen/logrus" + bolt "go.etcd.io/bbolt" +) + +var ( + starlarkRunBucketName = []byte("starlark-run-repository") + // there is only one key because there is only one Starlark Run object per APIC + starlarkRunKey = []byte("starlark-run-key") +) + +type StarlarkRunRepository struct { + enclaveDb *enclave_db.EnclaveDB +} + +func GetOrCreateNewStarlarkRunRepository(enclaveDb *enclave_db.EnclaveDB) (*StarlarkRunRepository, error) { + if err := enclaveDb.Update(func(tx *bolt.Tx) error { + bucket, err := tx.CreateBucketIfNotExists(starlarkRunBucketName) + if err != nil { + return stacktrace.Propagate(err, "An error occurred while creating the starlark run database bucket") + } + logrus.Debugf("Starlark run bucket: '%+v'", bucket) + + return nil + }); err != nil { + return nil, stacktrace.Propagate(err, "An error occurred while building the starlark run repository") + } + + starlarkRunRepository := &StarlarkRunRepository{ + enclaveDb: enclaveDb, + } + + return starlarkRunRepository, nil +} + +// Get will return either the starlark run or nil +func (repository *StarlarkRunRepository) Get() (*StarlarkRun, error) { + var ( + starlarkRunObj *StarlarkRun + err error + ) + + if err = repository.enclaveDb.View(func(tx *bolt.Tx) error { + bucket := tx.Bucket(starlarkRunBucketName) + + // first get the bytes + starlarkRunBytes := bucket.Get(starlarkRunKey) + + if starlarkRunBytes == nil { + return nil + } + + starlarkRunObj = &StarlarkRun{nil} + if err = json.Unmarshal(starlarkRunBytes, starlarkRunObj); err != nil { + return stacktrace.Propagate(err, "An error occurred unmarshalling starlark run") + } + + return nil + }); err != nil { + return nil, stacktrace.Propagate(err, "An error occurred while getting starlark run from the repository") + } + return starlarkRunObj, nil +} + +func (repository *StarlarkRunRepository) Save( + starlarkRun *StarlarkRun, +) error { + + if err := repository.enclaveDb.Update(func(tx *bolt.Tx) error { + bucket := tx.Bucket(starlarkRunBucketName) + + jsonBytes, err := json.Marshal(starlarkRun) + if err != nil { + return stacktrace.Propagate(err, "An error occurred marshalling starlark run '%+v'", starlarkRun) + } + + // save it to disk + if err := bucket.Put(starlarkRunKey, jsonBytes); err != nil { + return stacktrace.Propagate(err, "An error occurred while saving starlark run '%+v' into the enclave db bucket", starlarkRun) + } + + return nil + }); err != nil { + return stacktrace.Propagate(err, "An error occurred while saving starlark run '%+v' into the starlark run repository", starlarkRun) + } + return nil +} diff --git a/core/server/api_container/server/startosis_engine/starlark_run/repository_test.go b/core/server/api_container/server/startosis_engine/starlark_run/repository_test.go new file mode 100644 index 0000000000..452ae0b61f --- /dev/null +++ b/core/server/api_container/server/startosis_engine/starlark_run/repository_test.go @@ -0,0 +1,60 @@ +package starlark_run + +import ( + "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/database_accessors/enclave_db" + "github.com/stretchr/testify/require" + bolt "go.etcd.io/bbolt" + "os" + "testing" +) + +func TestSaveAndGetStarlarkRun_Success(t *testing.T) { + repository := getRepositoryForTest(t) + + originalStarlarRunForTest := NewStarlarkRun( + "github.com/kurtosis-tech/django-package", + "postgres = import_module(\"github.com/kurtosis-tech/postgres-package/main.star\")\ndjango_app = import_module(\"/app/app.star\")\n\n# Postgres defaults\nDEFAULT_POSTGRES_USER = \"postgres\"\nDEFAULT_POSTGRES_PASSWORD = \"secretdatabasepassword\"\nDEFAULT_POSTGRES_DB_NAME = \"django-db\"\nDEFAULT_POSTGRES_SERVICE_NAME = \"postgres\"\n\ndef run(\n plan,\n postgres_user=DEFAULT_POSTGRES_USER,\n postgres_password=DEFAULT_POSTGRES_PASSWORD,\n postgres_db_name=DEFAULT_POSTGRES_DB_NAME,\n postgres_service_name=DEFAULT_POSTGRES_SERVICE_NAME,\n):\n \"\"\"\n Starts this Django example application.\n\n Args:\n postgres_user (string): the Postgres's user name (default: postgres)\n postgres_password (string): the Postgres's password (default: secretdatabasepassword)\n postgres_db_name (string): the Postgres's db name (default: django-db)\n postgres_service_name (string): the Postgres's service name (default: postgres)\n \"\"\"\n\n # run the application's db\n postgres_db = postgres.run(\n plan,\n service_name=postgres_service_name,\n user=postgres_user,\n password=postgres_password,\n database=postgres_db_name,\n )\n\n # run the application's backend service\n django_app.run(plan, postgres_db, postgres_password)\n", + "{}", + 4, + "", + "", + []int32{2}, + int32(1), + "{}", + ) + + err := repository.Save(originalStarlarRunForTest) + require.NoError(t, err) + + starlarkRunFromRepository, err := repository.Get() + require.NoError(t, err) + + require.Equal(t, originalStarlarRunForTest, starlarkRunFromRepository) +} + +func TestGetNilStarlarkRun_Success(t *testing.T) { + repository := getRepositoryForTest(t) + + starlarkRunFromRepository, err := repository.Get() + require.NoError(t, err) + require.Nil(t, starlarkRunFromRepository) +} + +func getRepositoryForTest(t *testing.T) *StarlarkRunRepository { + file, err := os.CreateTemp("/tmp", "*.db") + defer func() { + err = os.Remove(file.Name()) + require.NoError(t, err) + }() + + require.NoError(t, err) + db, err := bolt.Open(file.Name(), 0666, nil) + require.NoError(t, err) + enclaveDb := &enclave_db.EnclaveDB{ + DB: db, + } + repository, err := GetOrCreateNewStarlarkRunRepository(enclaveDb) + require.NoError(t, err) + + return repository +} diff --git a/core/server/api_container/server/startosis_engine/starlark_run/starlark_run.go b/core/server/api_container/server/startosis_engine/starlark_run/starlark_run.go new file mode 100644 index 0000000000..99c3a69b8f --- /dev/null +++ b/core/server/api_container/server/startosis_engine/starlark_run/starlark_run.go @@ -0,0 +1,143 @@ +package starlark_run + +import ( + "encoding/json" + "github.com/kurtosis-tech/stacktrace" +) + +type StarlarkRun struct { + // we do this way in order to have exported fields which can be marshalled + // and an unexported type for encapsulation + privateStarlarkRun *privateStarlarkRun +} + +type privateStarlarkRun struct { + PackageId string + SerializedScript string + SerializedParams string + Parallelism int32 + RelativePathToMainFile string + MainFunctionName string + ExperimentalFeatures []int32 + RestartPolicy int32 + // The params that were used for the very first run of the script + InitialSerializedParams string +} + +func NewStarlarkRun( + packageId string, + serializedScript string, + serializedParams string, + parallelism int32, + relativePathToMainFile string, + mainFunctionName string, + experimentalFeatures []int32, + restartPolicy int32, + initialSerializedParams string, +) *StarlarkRun { + privateStarlarkRunObj := &privateStarlarkRun{ + PackageId: packageId, + SerializedScript: serializedScript, + SerializedParams: serializedParams, + Parallelism: parallelism, + RelativePathToMainFile: relativePathToMainFile, + MainFunctionName: mainFunctionName, + ExperimentalFeatures: experimentalFeatures, + RestartPolicy: restartPolicy, + InitialSerializedParams: initialSerializedParams, + } + + return &StarlarkRun{ + privateStarlarkRun: privateStarlarkRunObj, + } +} + +func (run *StarlarkRun) GetPackageId() string { + return run.privateStarlarkRun.PackageId +} + +func (run *StarlarkRun) SetPackageId(packageId string) { + run.privateStarlarkRun.PackageId = packageId +} + +func (run *StarlarkRun) GetSerializedScript() string { + return run.privateStarlarkRun.SerializedScript +} + +func (run *StarlarkRun) SetSerializedScript(serializedScript string) { + run.privateStarlarkRun.SerializedScript = serializedScript +} + +func (run *StarlarkRun) GetSerializedParams() string { + return run.privateStarlarkRun.SerializedParams +} + +func (run *StarlarkRun) SetSerializedParams(serializedParams string) { + run.privateStarlarkRun.SerializedParams = serializedParams +} + +func (run *StarlarkRun) GetParallelism() int32 { + return run.privateStarlarkRun.Parallelism +} + +func (run *StarlarkRun) SetParallelism(parallelism int32) { + run.privateStarlarkRun.Parallelism = parallelism +} + +func (run *StarlarkRun) GetRelativePathToMainFile() string { + return run.privateStarlarkRun.RelativePathToMainFile +} + +func (run *StarlarkRun) SetRelativePathToMainFile(relativePathToMainFile string) { + run.privateStarlarkRun.RelativePathToMainFile = relativePathToMainFile +} + +func (run *StarlarkRun) GetMainFunctionName() string { + return run.privateStarlarkRun.MainFunctionName +} + +func (run *StarlarkRun) SetMainFunctionName(mainFunctionName string) { + run.privateStarlarkRun.MainFunctionName = mainFunctionName +} + +func (run *StarlarkRun) GetExperimentalFeatures() []int32 { + return run.privateStarlarkRun.ExperimentalFeatures +} + +func (run *StarlarkRun) SetExperimentalFeatures(experimentalFeatures []int32) { + run.privateStarlarkRun.ExperimentalFeatures = experimentalFeatures +} + +func (run *StarlarkRun) GetRestartPolicy() int32 { + return run.privateStarlarkRun.RestartPolicy +} + +func (run *StarlarkRun) SetRestartPolicy(restartPolicy int32) { + run.privateStarlarkRun.RestartPolicy = restartPolicy +} + +func (run *StarlarkRun) GetInitialSerializedParams() string { + return run.privateStarlarkRun.InitialSerializedParams +} + +func (run *StarlarkRun) SetInitialSerializedParams(initialSerializedParams string) { + run.privateStarlarkRun.InitialSerializedParams = initialSerializedParams +} + +func (run *StarlarkRun) MarshalJSON() ([]byte, error) { + return json.Marshal(run.privateStarlarkRun) +} + +func (run *StarlarkRun) UnmarshalJSON(data []byte) error { + + // Suppressing exhaustruct requirement because we want an object with zero values + // nolint: exhaustruct + unmarshalledPrivateStructPtr := &privateStarlarkRun{} + + if err := json.Unmarshal(data, unmarshalledPrivateStructPtr); err != nil { + return stacktrace.Propagate(err, "An error occurred unmarshalling the private struct") + } + + run.privateStarlarkRun = unmarshalledPrivateStructPtr + return nil +} diff --git a/core/server/api_container/server/startosis_engine/starlark_run/starlark_run_test.go b/core/server/api_container/server/startosis_engine/starlark_run/starlark_run_test.go new file mode 100644 index 0000000000..da4b59d05b --- /dev/null +++ b/core/server/api_container/server/startosis_engine/starlark_run/starlark_run_test.go @@ -0,0 +1,51 @@ +package starlark_run + +import ( + "encoding/json" + "github.com/stretchr/testify/require" + "testing" +) + +func TestStarlarkRunMarshallers(t *testing.T) { + packageId := "package-id-test" + serializedScript := ` +def run(plan, args): + get_recipe = GetHttpRequestRecipe( + port_id = args["port_id"], + endpoint = args["endpoint"], + ) + plan.wait(recipe=get_recipe, field="code", assertion="==", target_value=200, interval=args["interval"], timeout=args["timeout"], service_name=args["service_name"]) +` + serializedParams := `{"greetings": "bonjour!"}` + parallelism := int32(2) + relativePathToMainFile := "" + mainFunctionName := "main" + experimentalFeatures := []int32{2, 7} + restartPolicy := int32(1) + initialSerializedParams := `{"bonjour": "foo"}` + + originalStarlarkRun := NewStarlarkRun( + packageId, + serializedScript, + serializedParams, + parallelism, + relativePathToMainFile, + mainFunctionName, + experimentalFeatures, + restartPolicy, + initialSerializedParams, + ) + + marshaledStarlarkRun, err := json.Marshal(originalStarlarkRun) + require.NoError(t, err) + require.NotNil(t, marshaledStarlarkRun) + + // Suppressing exhaustruct requirement because we want an object with zero values + // nolint: exhaustruct + newStarlarkRun := &StarlarkRun{} + + err = json.Unmarshal(marshaledStarlarkRun, newStarlarkRun) + require.NoError(t, err) + + require.EqualValues(t, originalStarlarkRun, newStarlarkRun) +}