Skip to content

Commit 47055fa

Browse files
Merge branch 'main' into feature/akri
2 parents 6c87fa2 + 13a8c28 commit 47055fa

File tree

35 files changed

+2066
-1547
lines changed

35 files changed

+2066
-1547
lines changed

.devcontainer/onCreateCommand.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ sudo apt-get update
1313
sudo apt-get install -y --no-install-recommends mosquitto-clients
1414

1515
# initialize the cluster
16-
tools/deployment/initialize-cluster.sh
16+
tools/deployment/initialize-cluster.sh -y
1717

1818
echo "Ending onCreateCommand"

.github/.linkspector.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ excludedFiles:
77
ignorePatterns:
88
# There are some dummy links, ignore these
99
- pattern: "^http://link_to_"
10+
aliveStatusCodes:
11+
- 200
12+
- 201
13+
- 204
14+
- 429 # Add 429 because we are rate limiting

.github/actions/configure-aio/action.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ runs:
3030
using: composite
3131
steps:
3232
- name: Create k3s cluster
33-
run: tools/deployment/initialize-cluster.sh
33+
run: tools/deployment/initialize-cluster.sh -y
3434
shell: bash
3535

3636
- name: Checkout deploy action
@@ -64,8 +64,10 @@ runs:
6464
- name: Setup Rust
6565
if: inputs.install-rust == 'true'
6666
uses: actions-rust-lang/setup-rust-toolchain@v1
67+
with:
68+
cache: false
6769

6870
- name: Wait for Mqtt Broker
6971
if: inputs.wait-for-broker == 'true'
70-
run: kubectl wait --for=create pod/aio-broker-frontend-0 --timeout=60s
72+
run: kubectl wait --for=create pod/aio-broker-frontend-0 --timeout=120s
7173
shell: bash

codegen/src/Azure.Iot.Operations.ProtocolCompiler/EnvoyGenerator/EnvoyTransformFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ private static IEnumerable<ITemplateTransform> GetCommandTransforms(string langu
320320

321321
if (normalResultSchema != null)
322322
{
323-
yield return new RustSerialization(respSchemaNamespace ?? genNamespace, genFormat, normalResultSchema, workingPath);
323+
yield return new RustSerialization(normalResultNamespace ?? genNamespace, genFormat, normalResultSchema, workingPath);
324324
}
325325

326326
break;

codegen/src/Azure.Iot.Operations.ProtocolCompiler/T4/communication/dotnet/Service/t4/DotNetService.tt

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ namespace <#=this.projectName#>.<#=this.genNamespace.GetTypeName(TargetLanguage.
5353
/// <param name="topicTokenMap">
5454
/// The topic token replacement map to use for all operations by default. Generally, this will include the token values
5555
/// for topic tokens such as "modelId" which should be the same for the duration of this service's lifetime. Note that
56-
/// additional topic tokens can be specified when starting the service with <see cref="StartAsync(Dictionary{string, string}?, int?, CancellationToken)"/> and
57-
/// can be specified per-telemetry message.
56+
/// additional topic tokens can be specified per-telemetry message.
5857
/// </param>
5958
public Service(ApplicationContext applicationContext, IMqttPubSubClient mqttClient, Dictionary<string, string>? topicTokenMap = null)
6059
{
@@ -139,20 +138,8 @@ namespace <#=this.projectName#>.<#=this.genNamespace.GetTypeName(TargetLanguage.
139138
/// <summary>
140139
/// Begin accepting command invocations for all command executors.
141140
/// </summary>
142-
/// <param name="additionalTopicTokenMap">
143-
/// The topic token replacements to use in addition to any topic tokens specified in the constructor. If this map
144-
/// contains any keys that topic tokens provided in the constructor also has, then values specified in this map will take precedence.
145-
/// </param>
146141
/// <param name="preferredDispatchConcurrency">The dispatch concurrency count for the command response cache to use.</param>
147142
/// <param name="cancellationToken">Cancellation token.</param>
148-
/// <remarks>
149-
/// Specifying custom topic tokens in <paramref name="additionalTopicTokenMap"/> allows you to make command executors only
150-
/// accept commands over a specific topic.
151-
///
152-
/// Note that a given command executor can only be started with one set of topic token replacements. If you want a command executor
153-
/// to only handle commands for several specific sets of topic token values (as opposed to all possible topic token values), then you will
154-
/// instead need to create a command executor per topic token set.
155-
/// </remarks>
156143
public async Task StartAsync(int? preferredDispatchConcurrency = null, CancellationToken cancellationToken = default)
157144
{
158145
string? clientId = this.mqttClient.ClientId;
@@ -247,8 +234,7 @@ namespace <#=this.projectName#>.<#=this.genNamespace.GetTypeName(TargetLanguage.
247234
/// <param name="mqttClient">The MQTT client to use.</param>
248235
/// <param name="topicTokenMap">
249236
/// The topic token replacement map to use for all operations by default. Generally, this will include the token values
250-
/// for topic tokens such as "modelId" which should be the same for the duration of this client's lifetime. Note that
251-
/// additional topic tokens can be specified when starting the client with <see cref="StartAsync(Dictionary{string, string}?, int?, CancellationToken)"/>.
237+
/// for topic tokens such as "modelId" which should be the same for the duration of this client's lifetime.
252238
/// </param>
253239
public Client(ApplicationContext applicationContext, IMqttPubSubClient mqttClient, Dictionary<string, string>? topicTokenMap = null)
254240
{
@@ -298,6 +284,12 @@ namespace <#=this.projectName#>.<#=this.genNamespace.GetTypeName(TargetLanguage.
298284
/// <summary>
299285
/// Invoke a command.
300286
/// </summary>
287+
<# if (this.doesCommandTargetExecutor) { #>
288+
/// <param name="executorId">The identifier of the executor targeted by this command request.</param>
289+
<# } #>
290+
<# if (cmdEnvoyInfo.RequestSchema != null) { #>
291+
/// <param name="request">The data for this command request.</param>
292+
<# } #>
301293
/// <param name="requestMetadata">The metadata for this command request.</param>
302294
/// <param name="additionalTopicTokenMap">
303295
/// The topic token replacement map to use in addition to the topic tokens specified in the constructor. If this map
@@ -336,19 +328,7 @@ namespace <#=this.projectName#>.<#=this.genNamespace.GetTypeName(TargetLanguage.
336328
/// <summary>
337329
/// Begin accepting telemetry for all telemetry receivers.
338330
/// </summary>
339-
/// <param name="additionalTopicTokenMap">
340-
/// The topic token replacements to use in addition to any topic tokens specified in the constructor. If this map
341-
/// contains any keys that topic tokens provided in the constructor also has, then values specified in this map will take precedence.
342-
/// </param>
343331
/// <param name="cancellationToken">Cancellation token.</param>
344-
/// <remarks>
345-
/// Specifying custom topic tokens in <paramref name="additionalTopicTokenMap"/> allows you to make telemetry receivers only
346-
/// accept telemetry over a specific topic.
347-
///
348-
/// Note that a given telemetry receiver can only be started with one set of topic token replacements. If you want a telemetry receiver
349-
/// to only handle telemetry for several specific sets of topic token values (as opposed to all possible topic token values), then you will
350-
/// instead need to create a telemetry receiver per topic token set.
351-
/// </remarks>
352332
public async Task StartAsync(CancellationToken cancellationToken = default)
353333
{
354334
await Task.WhenAll(

doc/dev/adr/0021-error-modeling-headers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# ADR 19: Modeling User Errors
1+
# ADR 21: User Error Headers
22

33
## Context
44

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
# ADR 22: Modeling Error Headers
2+
3+
## Context
4+
5+
[ADR 19][1] defines DTDL and codegen support for modeling user errors in MQTT payloads.
6+
[ADR 21][2] describes a facility for including user error information in MQTT headers, but it does not address how these headers could be modeled in DTDL.
7+
The present ADR enhances the modeling features defined in ADR 19 to enable modeling the user error headers described in ADR 21.
8+
9+
## Decision
10+
11+
The DTDL Mqtt extension, which is version 3 as of the implementation of ADR 19, will be enhanced as described herein, yielding version 4 of this extension.
12+
Implementing this mechanism will require changes to the ProtocolCompiler.
13+
14+
## MQTT extension, version 4
15+
16+
To enable models to express error headers in a way that can be understood by the ProtocolCompiler, the following new adjunct types are proposed for version 4 of the DTDL Mqtt extension.
17+
18+
| Adjunct Type | Material Cotype | Meaning |
19+
| --- | --- | --- |
20+
| `ErrorCode` | `Field` | Indicates that the cotyped `Field` within a `Result/Object` or an `Error/Object` defines a set of allowed values for the "AppErrCode" user property defined in ADR 21 |
21+
| `ErrorInfo` | `Field` | Indicates that the cotyped `Field` within a `Result/Object` or an `Error/Object` defines the schema for JSON-serialized values in the "AppErrPayload" user property defined in ADR 21 |
22+
23+
Use of these new types on `Field`s in a `Result/Object` is illustrated [below](#enhanced-model), and their use on on `Field`s in an `Error/Object` is illustrated [further below](#enhanced-model-with-errorresult).
24+
25+
## Sample model
26+
27+
The following DTDL model defines an "increment" command with a response schema that is an integer value named "counterValue".
28+
This is identical to the sample model in [ADR 19][1] except that the payload format has been changed from JSON to AVRO.
29+
The payload format was not especially relevant to ADR 19, but it is relevant to the present ADR.
30+
31+
```json
32+
{
33+
"@context": [ "dtmi:dtdl:context;4", "dtmi:dtdl:extension:mqtt;2" ],
34+
"@id": "dtmi:com:example:CounterCollection;1",
35+
"@type": [ "Interface", "Mqtt" ],
36+
"commandTopic": "rpc/command-samples/{executorId}/{commandName}",
37+
"payloadFormat": "Avro/1.11.0",
38+
"contents": [
39+
{
40+
"@type": "Command",
41+
"name": "increment",
42+
"request": {
43+
"name": "counterName",
44+
"schema": "string"
45+
},
46+
"response": {
47+
"name": "counterValue",
48+
"schema": "integer"
49+
}
50+
}
51+
]
52+
}
53+
```
54+
55+
## Enhanced model
56+
57+
The following DTDL model enhances the above model with error header information that is cotyped with some [extant adjunct types](./0019-codegen-user-errs.md#mqtt-extension-version-3) and the proposed [new adjunct types](#mqtt-extension-version-4).
58+
59+
```json
60+
{
61+
"@context": [ "dtmi:dtdl:context;4", "dtmi:dtdl:extension:mqtt;4" ],
62+
"@id": "dtmi:com:example:CounterCollection;1",
63+
"@type": [ "Interface", "Mqtt" ],
64+
"commandTopic": "rpc/command-samples/{executorId}/{commandName}",
65+
"payloadFormat": "Avro/1.11.0",
66+
"contents": [
67+
{
68+
"@type": "Command",
69+
"name": "increment",
70+
"request": {
71+
"name": "counterName",
72+
"schema": "string"
73+
},
74+
"response": {
75+
"name": "incrementResponse",
76+
"schema": {
77+
"@type": [ "Object", "Result" ],
78+
"fields": [
79+
{
80+
"@type": [ "Field", "NormalResult" ],
81+
"name": "counterValue",
82+
"schema": "integer"
83+
},
84+
{
85+
"@type": [ "Field", "ErrorCode" ],
86+
"name": "appErrCode",
87+
"schema": "dtmi:com:example:CounterCollection:AppErrCode;1"
88+
},
89+
{
90+
"@type": [ "Field", "ErrorInfo" ],
91+
"name": "appErrPayload",
92+
"schema": {
93+
"@type": "Array",
94+
"elementSchema": "string"
95+
}
96+
}
97+
]
98+
}
99+
}
100+
}
101+
],
102+
"schemas": [
103+
{
104+
"@id": "dtmi:com:example:CounterCollection:AppErrCode;1",
105+
"@type": "Enum",
106+
"valueSchema": "string",
107+
"enumValues": [
108+
{
109+
"name": "success",
110+
"enumValue": "succès"
111+
},
112+
{
113+
"name": "failure",
114+
"enumValue": "échec"
115+
}
116+
]
117+
}
118+
]
119+
}
120+
```
121+
122+
The extant `Result` adjunct type indicates that the `Object` that is modeled as the "schema" of the "response" is treated specially by the ProtocolCompiler, and each `Field` therein has a special meaning identified by its co-type.
123+
124+
As described in ADR 19, the `Field` co-typed `NormalResult` defines the result returned under normal conditions.
125+
If the above model were to include a `Field` co-typed `ErrorResult`, this would define the result returned under error conditions.
126+
When there is no `ErrorResult`, the response payload must always conform to the `NormalResult`, even when there is an error.
127+
128+
It is perfectly acceptable to use a `Field` co-typed `ErrorResult` within the same `Result/Object` as `Field`s co-typed with the new adjunct types defined herein, but this example omits the `ErrorResult` for simplicity.
129+
130+
Note that the absence of an `ErrorResult` permits the "payloadFormat" property to specify "raw/0" or "custom/0", even though these formats normally require the Command "response" value to have a "schema" of "bytes".
131+
Using a "response" "schema" of `Object/Result` with no `ErrorResult` and with a `NormalResult` "schema" of "bytes" permits the mechanism of the present ADR to define header values for raw and custom-serialized responses.
132+
133+
### ErrorCode adjunct type
134+
135+
A `Field` that is co-typed `ErrorCode` must have a "schema" that is an `Enum` whose "valueSchema" is "string".
136+
This `Enum` enumerates the allowed values for the "AppErrCode" user property defined in ADR 21.
137+
Each "name" will be code-generated into a language-appropriate enum name, and the corresponding "enumValue" will be the string used for the "AppErrCode" header value.
138+
139+
The `Field`'s "name" value ("appErrCode" in the above example) has no relevance to the communication, but it may affect the name of a code-generated type.
140+
141+
The specific usage of the generated enum type will vary according to programming language.
142+
For the C# code described in ADR 21, the `WithApplicationError` method on `ExtendedResponse` will have a code-generated form that accepts the enum type instead of a raw string for the error code.
143+
144+
If a modeled `Command` does not include an `ErrorCode` definition, user code is expected to provide a string value directly (as illustrated in ADR 21) instead of an enumerated value.
145+
146+
### ErrorInfo adjunct type
147+
148+
A `Field` that is co-typed `ErrorInfo` specifies the schema for information that will be communicated via the "AppErrPayload" user property defined in ADR 21.
149+
In the above example, this schema is an array of strings.
150+
The information provided by user code will be JSON-serialized into a string for the "AppErrPayload" header value.
151+
152+
Note that this JSON serialization is independent of the serialization format the model specifies for Command payloads.
153+
In the above example, the "payloadFormat" property has the value "Avro/1.11.0", indicating that AVRO serialization is used for Command (and Telemetry) payloads.
154+
To ensure that header values are legal UTF8 strings, JSON serialization is always used for the "AppErrPayload" value, as prescribed by ADR 19.
155+
156+
The `Field`'s "name" value ("appErrPayload" in the above example) has no relevance to the communication, but it may affect the name of a code-generated type.
157+
158+
The specific usage of the generated type will vary according to programming language.
159+
For the C# code described in ADR 21, the `WithApplicationError` method on `ExtendedResponse` will have a code-generated form that accepts the generated type instead of a serialized JSON string for the error info.
160+
161+
If a modeled `Command` does not include an `ErrorInfo` definition, user code is expected to provide a JSON-encoded string value directly (as illustrated in ADR 21) instead of a strongly typed value conformant to an `ErrorInfo` schema.
162+
163+
## Enhanced model with ErrorResult
164+
165+
The previous section described the use of adjunct types `ErrorCode` and `ErrorInfo` within an `Object` co-typed `Result`.
166+
These adjunct types may also be used within an `Object` co-typed `Error`.
167+
168+
The following DTDL model is similar to the above enhanced model, but it adds a `Field` with co-type `ErrorResult` to the `Object/Result`, similar to the example in ADR 19.
169+
Also, the `Field`s with new adjunct types have been relocated from the `Object/Result` to the `Object/Error` that is the "schema" of the `ErrorReslt`.
170+
171+
```json
172+
{
173+
"@context": [ "dtmi:dtdl:context;4", "dtmi:dtdl:extension:mqtt;4" ],
174+
"@id": "dtmi:com:example:CounterCollection;1",
175+
"@type": [ "Interface", "Mqtt" ],
176+
"commandTopic": "rpc/command-samples/{executorId}/{commandName}",
177+
"payloadFormat": "Avro/1.11.0",
178+
"contents": [
179+
{
180+
"@type": "Command",
181+
"name": "increment",
182+
"request": {
183+
"name": "counterName",
184+
"schema": "string"
185+
},
186+
"response": {
187+
"name": "incrementResponse",
188+
"schema": {
189+
"@type": [ "Object", "Result" ],
190+
"fields": [
191+
{
192+
"@type": [ "Field", "NormalResult" ],
193+
"name": "counterValue",
194+
"schema": "integer"
195+
},
196+
{
197+
"@type": [ "Field", "ErrorResult" ],
198+
"name": "incrementError",
199+
"schema": "dtmi:com:example:CounterCollection:CounterError;1"
200+
}
201+
]
202+
}
203+
}
204+
}
205+
],
206+
"schemas": [
207+
{
208+
"@id": "dtmi:com:example:CounterCollection:CounterError;1",
209+
"@type": [ "Object", "Error" ],
210+
"fields": [
211+
{
212+
"@type": [ "Field", "ErrorMessage" ],
213+
"name": "explanation",
214+
"schema": "string"
215+
},
216+
{
217+
"@type": [ "Field", "ErrorCode" ],
218+
"name": "appErrCode",
219+
"schema": "dtmi:com:example:CounterCollection:AppErrCode;1"
220+
},
221+
{
222+
"@type": [ "Field", "ErrorInfo" ],
223+
"name": "appErrPayload",
224+
"schema": {
225+
"@type": "Array",
226+
"elementSchema": "string"
227+
}
228+
}
229+
]
230+
},
231+
{
232+
"@id": "dtmi:com:example:CounterCollection:AppErrCode;1",
233+
"@type": "Enum",
234+
"valueSchema": "string",
235+
"enumValues": [
236+
{
237+
"name": "success",
238+
"enumValue": "succès"
239+
},
240+
{
241+
"name": "failure",
242+
"enumValue": "échec"
243+
}
244+
]
245+
}
246+
]
247+
}
248+
```
249+
250+
As described in ADR 19, the error processing path may differ from the normal processing path.
251+
For instance, in C#, the server indicates an error by throwing an exception type that is generated from the `Object` co-typed `Error`.
252+
The use of an exception prevents the server code from calling the `WithApplicationError` method because the `ExtendedResponse` is instantiated within the generated exception handler.
253+
Similarly, on the client side, exception-handling code has no access to the `ExtendedResponse`, so it is unable to call the `TryGetApplicationError` method thereon.
254+
255+
With the addition of `Field`s with co-types `ErrorCode` and `ErrorInfo` to the `Object/Error`, the exception type is generated with properties for setting and extracting values of the error headers.
256+
257+
[1]: ./0019-codegen-user-errs.md
258+
[2]: ./0021-error-modeling-headers.md

doc/reference/connection-settings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ The MqttConnectionSettings class enables the operator to configure the MQTT Conn
1313
|Name|Environment variable|Required|Type|Default value|Description|
1414
|-|-|-|-|-|-|
1515
|`Hostname`|`AIO_BROKER_HOSTNAME`|yes|string|n/a|FQDN to the endpoint, eg: mybroker.mydomain.com|
16-
|`TcpPort`|`AIO_BROKER_TCP_PORT`|no|uint16|`8883`|TCP port to access the endpoint eg: 8883|
16+
|`TcpPort`|`AIO_BROKER_TCP_PORT`|no|uint16|`18883`|TCP port to access the endpoint eg: 18883|
1717
|`UseTls`|`AIO_MQTT_USE_TLS`|no|bool|`true`|Enable TLS negotiation (disabling not recommended for production)|
1818
|`CaFile`|`AIO_TLS_CA_FILE`|no|string|null|Path to a PEM file to validate server identity|
1919
|`CleanStart`|`AIO_MQTT_CLEAN_START`|no|bool|false|Whether to use persistent session on first connect, subsequent connections will be `false`. `true` requires a unique `ClientId`.

0 commit comments

Comments
 (0)