Skip to content

Commit bc220e3

Browse files
authored
Add initial dashboard network protocol definition file (#1274)
* Add initial protocol definition file This `.proto` file defines a contract between the server `Aspire.Hosting` and client `Aspire.Dashboard`. It supports: - Getting basic information about the server (name and version). - Subscribing to resources, receiving both an initial snapshot and a stream of updates as they happen. - A heartbeat mechanism, to detect when the connection has dropped and allow prompting the user and/or reconnecting. - Support for arbitrary kinds of resources. Not limited to known types (project, container, executable). - Support for arbitrary commands on resources, intended for actions such as start/stop/refresh/restart/etc. The server defines the commands available for each resource, and the UI can show them. It's added to both the server and client projects, and code generation is enabled. * Remove heartbeat Turns out gRPC runs over HTTP/2 which includes a keep alive ping. We don't need to model those messages in our API thankfully. * Clarify ResourceType field documentation * Add optional display name to additional data * Review feedback * Add services for console and structured logs * Add TODO * Add comment * Remove obsolete TODO comment * Remove structured log messages This will be handled via OTLP. * Updates from review meeting * Split Endpoint and Service types These have different fields. * Rename messages and fields * Add .editorconfig to solution This allows opening the file more conveniently, and the file to participate in find-in-files.
1 parent 356591f commit bc220e3

File tree

4 files changed

+210
-0
lines changed

4 files changed

+210
-0
lines changed

Aspire.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CatalogDb", "samples\eShopL
145145
EndProject
146146
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{991DB378-6CB5-4441-BFC3-657400690FC3}"
147147
ProjectSection(SolutionItems) = preProject
148+
.editorconfig = .editorconfig
148149
Directory.Packages.props = Directory.Packages.props
149150
spelling.dic = spelling.dic
150151
EndProjectSection

src/Aspire.Dashboard/Aspire.Dashboard.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,11 @@
168168
<LastGenOutput>Routes.Designer.cs</LastGenOutput>
169169
</EmbeddedResource>
170170
</ItemGroup>
171+
172+
<ItemGroup>
173+
<Protobuf Include="..\Aspire.Hosting\Dashboard\proto\resource_service.proto" GrpcServices="Client" Access="Internal">
174+
<Link>Protos\resource_service.proto</Link>
175+
</Protobuf>
176+
</ItemGroup>
177+
171178
</Project>

src/Aspire.Hosting/Aspire.Hosting.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
</ItemGroup>
1515

1616
<ItemGroup>
17+
<PackageReference Include="Grpc.AspNetCore" />
1718
<PackageReference Include="KubernetesClient" />
1819
<PackageReference Include="Microsoft.Extensions.Hosting" />
1920
</ItemGroup>
@@ -46,4 +47,8 @@
4647
<InternalsVisibleTo Include="Aspire.Hosting.Tests" />
4748
</ItemGroup>
4849

50+
<ItemGroup>
51+
<Protobuf Include="Dashboard\proto\resource_service.proto" GrpcServices="Server" Access="Internal" />
52+
</ItemGroup>
53+
4954
</Project>
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
syntax = "proto3";
5+
6+
package aspire.v1;
7+
8+
import "google/protobuf/struct.proto";
9+
import "google/protobuf/timestamp.proto";
10+
11+
////////////////////////////////////////////
12+
13+
message ApplicationInformationRequest {
14+
}
15+
16+
message ApplicationInformationResponse {
17+
string application_name = 1;
18+
string application_version = 2;
19+
}
20+
21+
////////////////////////////////////////////
22+
23+
message ResourceCommandRequest {
24+
string command_type = 1;
25+
ResourceId resource_id = 2;
26+
// When present, this message must be shown to the user and their confirmation obtained
27+
// before sending the request for this command to be executed.
28+
// The user will be presented with Ok/Cancel options.
29+
optional string confirmation_message = 3;
30+
// Optional parameter that configures the command in some way.
31+
// Clients must return any value provided by the server when invoking
32+
// the command.
33+
optional google.protobuf.Value parameter = 4;
34+
}
35+
36+
enum ResourceCommandResponseKind {
37+
UNDEFINED = 0;
38+
SUCCEEDED = 1;
39+
FAILED = 2;
40+
CANCELLED = 3;
41+
}
42+
43+
message ResourceCommandResponse {
44+
ResourceCommandResponseKind kind = 1;
45+
optional string error_message = 2;
46+
}
47+
48+
////////////////////////////////////////////
49+
50+
message ResourceType {
51+
// Unique name for the resource type. Equivalent to ResourceId.resource_type
52+
// If "display_name" is omitted, this value will be used in UIs.
53+
string unique_name = 1;
54+
55+
// Display string for references to this type in UI. May be localized.
56+
// If this value is omitted, UIs will show "unique_name" instead.
57+
optional string display_name = 2;
58+
59+
// Any commands that may be executed against resources of this type, avoiding
60+
// the need to copy the value to every Resource instance.
61+
//
62+
// Note that these commands must apply to matching resources at any time.
63+
//
64+
// If the set of commands changes over time, use the "commands" property
65+
// of the Resource itself.
66+
repeated ResourceCommandRequest commands = 3;
67+
}
68+
69+
////////////////////////////////////////////
70+
71+
message EnvironmentVariable {
72+
string name = 1;
73+
optional string value = 2;
74+
optional bool is_from_spec = 3;
75+
}
76+
77+
message Endpoint {
78+
string endpoint_url = 1;
79+
string proxy_url = 2;
80+
}
81+
82+
message Service {
83+
string name = 1;
84+
optional string allocated_address = 2;
85+
optional int32 allocated_port = 3;
86+
optional string http_address = 4;
87+
}
88+
89+
message AdditionalData {
90+
// Name of the data item, e.g. "id", "image", "path", ...
91+
string name = 1;
92+
// Optional namespace, e.g. "container", "executable", "project", ...
93+
optional string namespace = 2;
94+
// Optional display name, may be localized
95+
optional string display_name = 3;
96+
// The data value. May be null, a number, a string, a boolean, a dictionary of values (Struct), or a list of values (ValueList).
97+
google.protobuf.Value value = 4;
98+
}
99+
100+
message ResourceId {
101+
string uid = 1;
102+
// TODO do we need resource_type to make unique names? if not, inline ResourceId type as string.
103+
string resource_type = 2;
104+
}
105+
106+
// Models the full state of an resource (container, executable, project, etc) at a particular point in time.
107+
message Resource {
108+
ResourceId resource_id = 1;
109+
string display_name = 2;
110+
optional string state = 3;
111+
optional google.protobuf.Timestamp created_at = 4;
112+
repeated EnvironmentVariable environment = 5;
113+
optional int32 expected_endpoints_count = 6;
114+
repeated Endpoint endpoints = 7;
115+
repeated Service services = 8;
116+
repeated ResourceCommandRequest commands = 9;
117+
118+
// List of additional data, as name/value pairs.
119+
// For:
120+
// - Containers: image, container_id, ports
121+
// - Executables: process_id, executable_path, working_directory, arguments
122+
// - Projects: process_id, project_path
123+
repeated AdditionalData additional_data = 10;
124+
}
125+
126+
////////////////////////////////////////////
127+
128+
// Models a snapshot of resource state
129+
message InitialResourceData {
130+
repeated Resource resources = 1;
131+
repeated ResourceType resource_types = 2;
132+
}
133+
134+
////////////////////////////////////////////
135+
136+
message ResourceDeletion {
137+
ResourceId resource_id = 1;
138+
}
139+
140+
message WatchResourcesChange {
141+
oneof kind {
142+
ResourceDeletion delete = 1;
143+
Resource upsert = 2;
144+
}
145+
}
146+
147+
message WatchResourcesChanges {
148+
repeated WatchResourcesChange value = 1;
149+
}
150+
151+
////////////////////////////////////////////
152+
153+
// Initiates a subscription for data about resources.
154+
message WatchResourcesRequest {
155+
// True if the client is establishing this connection because a prior one closed unexpectedly.
156+
optional bool is_reconnect = 1;
157+
}
158+
159+
// A message received from the server when watching resources. Has multiple types of payload.
160+
message WatchResourcesUpdate {
161+
oneof kind {
162+
// The current resource state, along with other reference data such as the set of resource types that may exist.
163+
// Received once upon connection, before any changes.
164+
InitialResourceData initial_data = 1;
165+
// One or more deltas to apply.
166+
WatchResourcesChanges changes = 2;
167+
}
168+
}
169+
170+
////////////////////////////////////////////
171+
172+
message ConsoleLogLine {
173+
string text = 1;
174+
// Indicates whether this line came from STDERR or not.
175+
optional bool is_std_err = 2;
176+
}
177+
178+
// Initiates a subscription for the logs of a resource.
179+
message WatchResourceConsoleLogsRequest {
180+
// Specifies the resource to watch logs from.
181+
ResourceId resource_id = 1;
182+
}
183+
184+
// A message received from the server when watching resource logs.
185+
// Contains potentially many lines to be appended to the log.
186+
message WatchResourceConsoleLogsUpdate {
187+
repeated ConsoleLogLine log_lines = 1;
188+
}
189+
190+
////////////////////////////////////////////
191+
192+
service DashboardService {
193+
rpc GetApplicationInformation(ApplicationInformationRequest) returns (ApplicationInformationResponse);
194+
rpc WatchResources(WatchResourcesRequest) returns (stream WatchResourcesUpdate);
195+
rpc WatchResourceConsoleLogs(WatchResourceConsoleLogsRequest) returns (stream WatchResourceConsoleLogsUpdate);
196+
rpc ExecuteResourceCommand(ResourceCommandRequest) returns (ResourceCommandResponse);
197+
}

0 commit comments

Comments
 (0)