From 4e9bc1bbe2c1bfb1fe567999d6219e6a6807897d Mon Sep 17 00:00:00 2001 From: Karol Zadora-Przylecki Date: Mon, 13 Nov 2023 14:01:16 -0800 Subject: [PATCH 1/2] Add IDE execution doc --- docs/specs/IDE-execution.md | 146 ++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 docs/specs/IDE-execution.md diff --git a/docs/specs/IDE-execution.md b/docs/specs/IDE-execution.md new file mode 100644 index 0000000000..efdfeb5dfc --- /dev/null +++ b/docs/specs/IDE-execution.md @@ -0,0 +1,146 @@ +# External execution of programs (a.k.a. IDE execution) + +By default, `Executable` objects that are part of DCP workload are run as ordinary operating system processes. That means, DCP uses appropriate OS call to start a child process that executes the program specified by `Executable` spec. For debugging application services this is often not convenient or useful: it is difficult to debug service startup code this way, or restart the process automatically when source code changes. This is why DCP supports an alternative method of running `Executable` object, which is called **IDE execution**. + +With IDE execution DCP delegates the task of running the program represented by `Executable` object to an external entity. The entity can be an IDE such as Visual Studio or Visual Studio Code, or a CLI tool like `dotnet watch`, but in the rest of this document we will refer to it as **IDE**. + +When IDE execution is enabled for an `Executable` object, DCP will go through normal execution preparation steps (such as computing the values of environment variables for the `Executable`), but instead of creating an OS process, DCP will issue a request to the IDE to run the `Executable` program, passing all relevant information in the request. DCP then relies on the IDE to start the program, creating a **run session**. The IDE is expected to inform DCP about changes to the session (e.g. program finished execution) and DCP reflects these changes in the workload model. + +## Enabling IDE execution + +For IDE execution to work, two conditions need to be fulfilled: + +1. DCP needs to be told how to contact the IDE (what is the **IDE session endpoint**, specifically). +1. The `ExecutionType` property for the `Executable` object needs to be set to `IDE` (default is `Process`, which indicates OS process-based execution). + +Only one IDE (one IDE session endpoint) is supported per DCP instance. The IDE session endpoint is provided to DCP via environment variables (both required if IDE execution is used): + +| Environment variable | Value | +| ----- | ----- | +| `DEBUG_SESSION_PORT` | The port DCP should use to talk to the IDE session endpoint. DCP will use `http://localhost:` as the IDE session endpoint base URL. | +| `DEBUG_SESSION_TOKEN` | Security (bearer) token for talking to the IDE session endpoint. This token will be attached to every request via Authorization HTTP header. | + +> Note: the most important use case for the IDE execution is to facilitate application services debugging. The word "debug" appears in environment variable names that DCP uses to connect to IDE session endpoint, but IDE execution does not always mean that the service is running under a debugger. + +### Using multiple execution types in the same workload + +It is possible to have a workload that uses both IDE execution for some `Executable` objects, and process execution for other `Executable` objects. Every `Executable` object is treated separately according to the `ExecutionType` spec property. + +If multiple replicas are used (created via `ExecutableReplicaSet` objects), all replicas belonging to the same replica set will use the same execution type. + +## IDE session endpoint requests + +The IDE session endpoint is expected to support the following requests, delivered via HTTP protocol, and using JSON as the payload format: + +### Create session request + +Used to create a new run session for particular `Executable`. + +**HTTP verb and path**
+`PUT /run_session` + +**Headers**
+`Authorization: Bearer `
+`Content-Type: application/json` + +**Payload**
+ +The payload is best explained using an example: + +```jsonc +{ + "project_path": "", + "debug": true, // Whether the program should be running under the debugger + "env" : [ + // Environment variables are modeled as objects, with 'name' and 'value' property, for example: + { "name": "NO_COLOR", "value": "1" }, + { "name": "EMPTY_VALUE_VAR", "value": "" } + ], + "args": [ + // Invocation arguments for the program (modeled as array of strings) + "-v", + "1" + ], +} +``` + +**Response**
+If the execution session is created successfully, the return status code should be 200 OK or 201 Created. The response should include the created run session identifier in the `Location` header: + +`Location: http://localhost:/run_session/` + +If the session cannot be created, appropriate 4xx or 5xx status code should be returned. The response might also return a description of the problem as part of the status line, or in the response body. + +### Stop session request + +Used to stop an in-progress run session + +**HTTP verb and path**
+`DELETE /run_session/` + +**Headers**
+`Authorization: Bearer ` + +**Response**
+If the session exists and can be stopped, the IDE should reply with 200 OK status code. + +If the session does not exist, the IDE should reply with 204 No Content. + +If the session cannot be stopped, appropriate 4xx or 5xx status code should be returned. The response might also return a description of the problem as part of the status line, or in the response body. + +### Subscribe to session change notifications request + +Used by DCP to subscribe to run session change notification. + +**HTTP verb and path**
+`GET /run_session/notify` + +**Headers**
+`Authorization: Bearer `
+(+ web sockets connection upgrade headers) + +**Response**
+If successful, the connection should be upgraded to a web sockets connection, which will be then used by the IDE to stream run session change notifications to DCP. See next paragraph for description of possible change notifications. + +## Run session change notifications + +The run session change notifications are delivered from IDE to DCP via the web socket connection. The format of notification is JSON Lines (one JSON object per line of text). + +All run sessions share the same notification stream. Notifications are delivered in near-real-time as sessions change--there is no memory/reply of notifications for sessions that occurred in the past. DCP subscribes to run session change notifications before it makes the first request to create a run session; this ensures that it will receive all change notifications for all sessions it creates. + +### Common notification properties + +Every run change notification has the following properties: + +| Property | Description | Type | +| --- | --------- | --- | +| `notification_type` | One of `processRestarted`, `sessionTerminated`, or `serviceLogs`: indicates the type of notification. | `string` (limited set of values) | +| `session_id` | The ID of the run session that the notification is related to. | `string` | + +### Process restarted notification + +The process (re)started notification in emitted when the run is started, and whenever the IDE restarts the service (upon developer request). Properties specific to this notification are: + +| Property | Description | Type | +| --- | --------- | --- | +| `notification_type` | Must be `processRestarted` | `string` | +| `pid` | The process ID of the service process associated with the run session. | `number` (representing unsigned 32-bit integer) | + +### Session terminated notification + +Session terminated notification is emitted when the session is terminated (the program ends, or is terminated by the developer). Properties specific to this notification are: + +| Property | Description | Type | +| --- | --------- | --- | +| `notification_type` | Must be `sessionTerminated` | `string` | +| `exit_code` | The exit code of the process associated with the run session. | `number` (representing unsigned 32-bit integer) | + +### Log notification + +The log notification is emitted when the service program writes something to standard output stream (`stdout`) or standard error (`stderr`). Properties specific to this notification are: + +| Property | Description | Type | +| --- | --------- | --- | +| `notification_type` | Must be `serviceLogs` | `string` | +| `is_std_err` | True if the output comes from standard error stream, otherwise false (implying standard output stream). | `boolean` | +| `log_message` | The text written by the service program. | `string` | From d801baafb2b0b0c60bcc5704281aeaa1723c6aa2 Mon Sep 17 00:00:00 2001 From: Karol Zadora-Przylecki Date: Mon, 13 Nov 2023 14:22:07 -0800 Subject: [PATCH 2/2] Introduce DCP in the IDE execution doc --- docs/specs/IDE-execution.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/specs/IDE-execution.md b/docs/specs/IDE-execution.md index efdfeb5dfc..5113801499 100644 --- a/docs/specs/IDE-execution.md +++ b/docs/specs/IDE-execution.md @@ -1,4 +1,9 @@ -# External execution of programs (a.k.a. IDE execution) +# Running .NET Aspire applications inside an integrated developer environment (IDE) + +## Application host, DCP, and IDE +When .NET Aspire application host program is run, it does not launch application service programs or supporting emulators/containers directly. Instead, the application host relies on another program ("app orchestrator") called `DCP`. + +When Visual Studio (or another IDE) starts the Aspire application host, Aspire-specific application model is created from user code in app host project. This model is then converted to another, language-agnostic model that DCP understands, and is submitted by app host to DCP for execution. The models are quite similar: for example, Aspire project (application services) are modeled as `Executable` objects on DCP side; containers become `Container` objects in DCP world. By default, `Executable` objects that are part of DCP workload are run as ordinary operating system processes. That means, DCP uses appropriate OS call to start a child process that executes the program specified by `Executable` spec. For debugging application services this is often not convenient or useful: it is difficult to debug service startup code this way, or restart the process automatically when source code changes. This is why DCP supports an alternative method of running `Executable` object, which is called **IDE execution**.