Skip to content

Commit 3ae8105

Browse files
authored
Merge pull request #1083 from Gijsreyn/add-secret-schema
(SCHEMA) Add extension secret to schema source
2 parents b081a22 + ac477d8 commit 3ae8105

File tree

3 files changed

+356
-0
lines changed

3 files changed

+356
-0
lines changed
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
2+
$schema: https://json-schema.org/draft/2020-12/schema
3+
$id: <HOST>/<PREFIX>/<VERSION>/extension/manifest.secret.yaml
4+
5+
title: Secret operation command
6+
description: >-
7+
Defines how DSC must call the DSC extension to retrieve a secret value.
8+
markdownDescription: | # VS Code only
9+
***
10+
[_Online Documentation_][00]
11+
***
12+
13+
Defines how DSC must call the DSC extension to retrieve a secret value. An extension that defines
14+
this field in its manifest has the `secret` capability.
15+
16+
DSC expects extensions implementing the `secret` capability to adhere to the following contract:
17+
18+
1. If the extension retrieves the secret, the extension must emit the secret to stdout as a
19+
single line of plaintext and exit with code `0`. DSC consumes the emitted output and makes the
20+
secret available in the configuration document.
21+
22+
If the extension emits more than one line to stdout, DSC raises an error.
23+
24+
1. If the extension cannot retrieve the secret because the secret doesn't exist, the extension
25+
must not emit any text to stdout and must exit with code `0`. DSC interprets this result as
26+
the secret not existing in the vault.
27+
28+
1. If the extension cannot retrieve the secret for any other reason, such as invalid credentials
29+
or an API error, the extension should emit a descriptive error message as a JSON Line to
30+
stderr and exit with a nonzero exit code. DSC interprets the nonzero exit code as an
31+
operational failure and surfaces that information and any emitted error messages to the user.
32+
33+
When the exit code for the operation is `0`, DSC interprets the operation as completing without
34+
errors. For extensions, failure to retrieve a secret because it doesn't exist is _not_ an error.
35+
Failure to retrieve a secret for any other reason _is_ an error and the extension should exit
36+
with a nonzero code. For an improved user experience, the extension should define the `exitCodes`
37+
field in the extension manifest to indicate what the nonzero exit code means.
38+
39+
For more information about how DSC validates the data for stdout, see
40+
[Secret extension operation stdout][01]. For more information about defining exit codes for the
41+
extension, see [`exitCodes`][02] in the extension manifest schema reference.
42+
43+
[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>
44+
[01]: <DOCS_BASE_URL>/reference/schemas/extension/stdout/secret?<DOCS_VERSION_PIN>
45+
[02]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/root?<DOCS_VERSION_PIN>#exitcodes
46+
47+
type: object
48+
required:
49+
- executable
50+
- args
51+
properties:
52+
executable:
53+
$ref: /<PREFIX>/<VERSION>/definitions/commandExecutable.yaml
54+
markdownDescription: |
55+
***
56+
[_Online Documentation_][00]
57+
***
58+
59+
Defines the name of the command to run. The value must be the name of a command discoverable
60+
in the system's `PATH` environment variable or the full path to the command. A file extension
61+
is only required when the command isn't recognizable by the operating system as an
62+
executable.
63+
64+
[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#executable
65+
args:
66+
title: Arguments
67+
description: >-
68+
Defines an ordered list of arguments to pass to the command.
69+
markdownDescription: |
70+
***
71+
[_Online Documentation_][00]
72+
***
73+
74+
Defines an ordered list of arguments to pass to the command. DSC passes each defined item to
75+
the executable in the order you define them. Items can be string arguments, the secret name
76+
input argument, or the vault input argument.
77+
78+
In order for DSC to retrieve a secret with the extension, the manifest must define the secret
79+
name input argument. For DSC to retreieve a secret from a specific vault with the extension,
80+
the manifest must define the vault input argument.
81+
82+
For example, given the following manifest snippet:
83+
84+
```jsonc
85+
{
86+
// ellided extension manifest fields
87+
"secret": {
88+
"executable": "my_secret_extension",
89+
"args": [
90+
"get",
91+
{ "nameArg": "--secretName" }
92+
]
93+
}
94+
}
95+
```
96+
97+
When DSC invokes the extension to retrieve a secret named `apiToken`, it constructs
98+
the following effective command:
99+
100+
```bash
101+
my_secret_extension get --secretName apiToken
102+
```
103+
104+
If the user needs to retrieve a secret from a specific vault, DSC is unable to pass the vault
105+
name to the previously defined snippet. The following snippet supports passing the vault name
106+
to the extension as well as the secret name:
107+
108+
```jsonc
109+
{
110+
// ellided extension manifest fields
111+
"secret": {
112+
"executable": "my_secret_extension",
113+
"args": [
114+
"get",
115+
{ "nameArg": "--secretName" },
116+
{ "vaultArg": "--vaultName" }
117+
]
118+
}
119+
}
120+
```
121+
122+
When DSC invokes the extension to retrieve a secret named `apiToken` from the `services`
123+
vault, it constructs the following effective command:
124+
125+
```bash
126+
my_secret_extension get --secretName apiToken --vaultName services
127+
```
128+
129+
[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#args
130+
type: array
131+
items:
132+
oneOf:
133+
- title: String argument
134+
description: >-
135+
Any item in the argument array can be a string representing a static argument to pass
136+
to the command, like `get` or `--quiet`.
137+
markdownDescription: |-
138+
***
139+
[_Online Documentation_][00]
140+
***
141+
142+
Any item in the argument array can be a string representing a static argument to pass
143+
to the command, like `get` or `--quiet`.
144+
145+
[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#string-arguments
146+
type: string
147+
minLength: 1
148+
- type: object
149+
title: Secret name input argument
150+
description: >-
151+
Defines the argument for the command that accepts the name of a secret.
152+
markdownDescription: |-
153+
***
154+
[_Online Documentation_][00]
155+
***
156+
157+
Defines the argument for the command that accepts the name of a secret. DSC passes the
158+
name of the secret as a string after the defined argument. You must define this
159+
argument as an object with the `nameArg` property set to the appropriate argument for
160+
the extension command, like `--secret` or `--secret-name`.
161+
162+
An extension implementing the `secret` capability _must_ define a secret name input
163+
argument exactly once.
164+
165+
[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#secret-name-input-argument
166+
unevaluatedProperties: false
167+
required:
168+
- nameArg
169+
properties:
170+
nameArg:
171+
title: Secret name input argument definition
172+
description: >-
173+
Defines the literal string for the argument that accepts the name of the secret to
174+
retrieve, like `--name` or `--secret-name`.
175+
markdownDescription: |-
176+
Defines the literal string for the argument that accepts the name of the secret to
177+
retrieve, like `--name` or `--secret-name`.
178+
type: string
179+
minLength: 1
180+
- type: object
181+
title: Vault input argument
182+
description: >-
183+
Defines the argument for the command that accepts the name of a specific vault to
184+
retrieve a secret from.
185+
markdownDescription: |-
186+
***
187+
[_Online Documentation_][00]
188+
***
189+
190+
Defines the argument for the command that accepts the name of a specific vault to
191+
retrieve a secret from. Define this argument as an object with the `vaultArg` property
192+
set to the appropriate argument for the extension command, like `--vault` or
193+
`--vault-name`.
194+
195+
To support retrieving secrets from a specific vault, an extension implementing the
196+
`secret` capability _must_ define a vault input argument exactly once.
197+
198+
[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#vault-input-argument
199+
unevaluatedProperties: false
200+
required:
201+
- vaultArg
202+
properties:
203+
vaultArg:
204+
title: Vault input argument definition
205+
description: >-
206+
Defines the literal string for the argument that accepts the name of the vault to
207+
retrieve a secret from, like `--vault` or `--vault-name`.
208+
markdownDescription: |-
209+
Defines the literal string for the argument that accepts the name of the vault to
210+
retrieve a secret from, like `--vault` or `--vault-name`.
211+
type: string
212+
minLength: 1
213+
214+
# Need to use an allOf with multiple possibilities to capture the requirement to define the secret
215+
# name input argument exactly once and the vault input argument no more than once. Note that
216+
# the YAML language server in VS Code currently doesn't understand the `minContains` and
217+
# `maxContains` keywords, so when defining the extension manifest in YAML with the schema, the
218+
# language server identifies the vault input argument as required. When defining the extension
219+
# manifest in JSON, the language server correctly identifies the minimum and maximum number of
220+
# arguments to use.
221+
#
222+
# Unfortunately, because we only get one error message per item in the `allOf` keyword, we have to
223+
# define two entries for the secret name input argument to provide better validation error messages.
224+
#
225+
# We use long lines for error messages, which can't use Markdown, so line breaks are literal.
226+
allOf:
227+
- title: Missing secret name input argument
228+
$comment: >-
229+
This validation subschema ensures that `secret.args` contains a secret name input argument.
230+
Without this argument, DSC can't pass the name of the secret to retrieve when invoking the
231+
extension's secret command.
232+
233+
We define this separately from the maximum contains validation subschema to improve the error
234+
messaging in VS Code.
235+
properties:
236+
args:
237+
errorMessage: |-
238+
The `secret` command doesn't define the secret name input argument. If you don't define the secret name input argument, DSC can't pass the secret name to the extension for retrieval. You can only define one secret name input argument for the command.
239+
240+
You must define one argument in `secret.args` as a JSON object with the `nameArg` property. For more information, see:
241+
242+
<DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>
243+
contains:
244+
type: object
245+
required: [nameArg]
246+
minContains: 1
247+
- title: Multiple secret name input arguments
248+
$comment: >-
249+
This validation subschema ensures that `secret.args` doesn't contain more than one secret
250+
name input argument. Defining more than one secret name input argument is invalid.
251+
252+
We define this separately from the minimum contains validation subschema to improve the error
253+
messaging in VS Code.
254+
properties:
255+
args:
256+
errorMessage: |-
257+
The `secret` command defines the secret name input argument more than once. You can only define one secret name input argument for the command.
258+
259+
You must define one argument in `secret.args` as a JSON object with the `nameArg` property and remove the additional secret name input arguments. For more information, see:
260+
261+
<DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>
262+
contains:
263+
type: object
264+
required: [nameArg]
265+
maxContains: 1
266+
- title: Multiple vault input arguments
267+
$comment: >-
268+
This validation subschema ensures that `secret.args` contains zero or one vault input
269+
arguments. Defining a vault input argument is optional. Extensions that don't define the
270+
vault input argument don't support retrieving secrets from a specific vault. Defining
271+
more than one vault input argument is invalid.
272+
properties:
273+
args:
274+
errorMessage: |-
275+
The `secret` command defines the vault input argument more than once. If you don't define the vault input argument, DSC can't pass the vault to the extension for retrieving a secret from a specific vault. You can only define one vault input argument for the command.
276+
277+
You can define one argument in `secret.args` as a JSON object with the `vaultArg` property. For more information, see:
278+
279+
<DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>
280+
contains:
281+
type: object
282+
required: [vaultArg]
283+
minContains: 0
284+
maxContains: 1
285+
286+
defaultSnippets: # VS Code only
287+
- label: ' Define with arguments (static string and secret name input arguments)'
288+
markdownDescription: |-
289+
Define the `secret` command where the secret name is passed to the command. Use this
290+
snippet if the extension doesn't support retrieving a secret from a specific vault.
291+
body:
292+
executable: ${1:executable_name}
293+
args:
294+
- ${2:get}
295+
- nameArg: ${3:--secret-name}
296+
- label: ' Define with arguments (static string, secret name, and vault input arguments)'
297+
markdownDescription: |-
298+
Define the `secret` command where both the secret name and the vault name are passed to the
299+
command. Use this snippet if the extension supports retrieving a secret from a specific vault.
300+
body:
301+
executable: ${1:executable_name}
302+
args:
303+
- ${2:get}
304+
- nameArg: ${3:--secret-name}
305+
- vaultArg: ${4:--vault-name}

schemas/src/extension/manifest.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,8 @@ properties:
459459
characters are permitted.
460460
discover:
461461
$ref: /<PREFIX>/<VERSION>/extension/manifest.discover.yaml
462+
secret:
463+
$ref: /<PREFIX>/<VERSION>/extension/manifest.secret.yaml
462464
exitCodes:
463465
# This setting in the root of the schema implies exit codes must have the
464466
# same meaning across all executions. What about implementations that
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
2+
$schema: https://json-schema.org/draft/2020-12/schema
3+
$id: <HOST>/<PREFIX>/<VERSION>/extension/stdout/secret.yaml
4+
5+
title: Secret extension operation stdout
6+
description: >-
7+
Represents the secret text returned by a DSC extension for a secret request.
8+
markdownDescription: | # VS Code only
9+
***
10+
[_Online Documentation_][00]
11+
***
12+
13+
Represents the secret text returned by a DSC extension for a secret request. The output that
14+
DSC expects from the extension depends on the result of retrieving the secret:
15+
16+
- If the extension successfully retrieves the secret, DSC expects the extension to:
17+
18+
1. Emit a single line containing only the secret value, not wrapped in quotation marks or a
19+
JSON object, to stdout. DSC treats the literal text returned by the extension as the secret
20+
value. If the emitted output contains any newlines, the output is invalid.
21+
1. Exit with code `0` to indicate success.
22+
23+
- If the extension can't retrieve the secret because no secret with the given name was
24+
discovered, DSC expects the extension to:
25+
26+
1. Emit _no output_ to stdout. If the extension emits any output to stdout, DSC interprets the
27+
emitted text to be the secret value.
28+
1. Optionally emit messages to stderr as [JSON Lines][01] to indicate the attempt to retrieve
29+
the secret and noting that the secret doesn't exist. DSC surfaces emitted messages to the
30+
caller depending on the message level.
31+
1. Exit with code `0` to indicate success. For DSC, an extension not returning a secret value
32+
because no secret with the given name exists is _not_ a failure.
33+
34+
- If the extension can't retrieve the secret for any other reason, such as requiring the vault
35+
to be unlocked or encountering an API error, DSC expects the extension to:
36+
37+
1. Emit _no output_ to stdout.
38+
1. Optionally emit detailed error messages to stderr as [JSON Lines][01] to provide contextual
39+
information about how and why the retrieval failed. DSC always surfaces emitted error
40+
messages to the caller.
41+
1. Exit with a nonzero code to indicate failure. If the extension manifest defines the
42+
[`exitCodes`][02] field, DSC surfaces the meaning of the exit code to the caller.
43+
44+
[00]: <DOCS_BASE_URL>/reference/schemas/extension/stdout/secret?<DOCS_VERSION_PIN>
45+
[01]: https://jsonlines.org/
46+
[02]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/root?<DOCS_VERSION_PIN>#exitcodes
47+
48+
type: string
49+
minLength: 0

0 commit comments

Comments
 (0)