Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
305 changes: 305 additions & 0 deletions schemas/src/extension/manifest.secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
$schema: https://json-schema.org/draft/2020-12/schema
$id: <HOST>/<PREFIX>/<VERSION>/extension/manifest.secret.yaml

title: Secret operation command
description: >-
Defines how DSC must call the DSC extension to retrieve a secret value.
markdownDescription: | # VS Code only
***
[_Online Documentation_][00]
***

Defines how DSC must call the DSC extension to retrieve a secret value. An extension that defines
this field in its manifest has the `secret` capability.

DSC expects extensions implementing the `secret` capability to adhere to the following contract:

1. If the extension retrieves the secret, the extension must emit the secret to stdout as a
single line of plaintext and exit with code `0`. DSC consumes the emitted output and makes the
secret available in the configuration document.

If the extension emits more than one line to stdout, DSC raises an error.

1. If the extension cannot retrieve the secret because the secret doesn't exist, the extension
must not emit any text to stdout and must exit with code `0`. DSC interprets this result as
the secret not existing in the vault.

1. If the extension cannot retrieve the secret for any other reason, such as invalid credentials
or an API error, the extension should emit a descriptive error message as a JSON Line to
stderr and exit with a nonzero exit code. DSC interprets the nonzero exit code as an
operational failure and surfaces that information and any emitted error messages to the user.

When the exit code for the operation is `0`, DSC interprets the operation as completing without
errors. For extensions, failure to retrieve a secret because it doesn't exist is _not_ an error.
Failure to retrieve a secret for any other reason _is_ an error and the extension should exit
with a nonzero code. For an improved user experience, the extension should define the `exitCodes`
field in the extension manifest to indicate what the nonzero exit code means.

For more information about how DSC validates the data for stdout, see
[Secret extension operation stdout][01]. For more information about defining exit codes for the
extension, see [`exitCodes`][02] in the extension manifest schema reference.

[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>
[01]: <DOCS_BASE_URL>/reference/schemas/extension/stdout/secret?<DOCS_VERSION_PIN>
[02]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/root?<DOCS_VERSION_PIN>#exitcodes

type: object
required:
- executable
- args
properties:
executable:
$ref: /<PREFIX>/<VERSION>/definitions/commandExecutable.yaml
markdownDescription: |
***
[_Online Documentation_][00]
***

Defines the name of the command to run. The value must be the name of a command discoverable
in the system's `PATH` environment variable or the full path to the command. A file extension
is only required when the command isn't recognizable by the operating system as an
executable.

[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#executable
args:
title: Arguments
description: >-
Defines an ordered list of arguments to pass to the command.
markdownDescription: |
***
[_Online Documentation_][00]
***

Defines an ordered list of arguments to pass to the command. DSC passes each defined item to
the executable in the order you define them. Items can be string arguments, the secret name
input argument, or the vault input argument.

In order for DSC to retrieve a secret with the extension, the manifest must define the secret
name input argument. For DSC to retreieve a secret from a specific vault with the extension,
the manifest must define the vault input argument.

For example, given the following manifest snippet:

```jsonc
{
// ellided extension manifest fields
"secret": {
"executable": "my_secret_extension",
"args": [
"get",
{ "nameArg": "--secretName" }
]
}
}
```

When DSC invokes the extension to retrieve a secret named `apiToken`, it constructs
the following effective command:

```bash
my_secret_extension get --secretName apiToken
```

If the user needs to retrieve a secret from a specific vault, DSC is unable to pass the vault
name to the previously defined snippet. The following snippet supports passing the vault name
to the extension as well as the secret name:

```jsonc
{
// ellided extension manifest fields
"secret": {
"executable": "my_secret_extension",
"args": [
"get",
{ "nameArg": "--secretName" },
{ "vaultArg": "--vaultName" }
]
}
}
```

When DSC invokes the extension to retrieve a secret named `apiToken` from the `services`
vault, it constructs the following effective command:

```bash
my_secret_extension get --secretName apiToken --vaultName services
```

[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#args
type: array
items:
oneOf:
- title: String argument
description: >-
Any item in the argument array can be a string representing a static argument to pass
to the command, like `get` or `--quiet`.
markdownDescription: |-
***
[_Online Documentation_][00]
***

Any item in the argument array can be a string representing a static argument to pass
to the command, like `get` or `--quiet`.

[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#string-arguments
type: string
minLength: 1
- type: object
title: Secret name input argument
description: >-
Defines the argument for the command that accepts the name of a secret.
markdownDescription: |-
***
[_Online Documentation_][00]
***

Defines the argument for the command that accepts the name of a secret. DSC passes the
name of the secret as a string after the defined argument. You must define this
argument as an object with the `nameArg` property set to the appropriate argument for
the extension command, like `--secret` or `--secret-name`.

An extension implementing the `secret` capability _must_ define a secret name input
argument exactly once.

[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#secret-name-input-argument
unevaluatedProperties: false
required:
- nameArg
properties:
nameArg:
title: Secret name input argument definition
description: >-
Defines the literal string for the argument that accepts the name of the secret to
retrieve, like `--name` or `--secret-name`.
markdownDescription: |-
Defines the literal string for the argument that accepts the name of the secret to
retrieve, like `--name` or `--secret-name`.
type: string
minLength: 1
- type: object
title: Vault input argument
description: >-
Defines the argument for the command that accepts the name of a specific vault to
retrieve a secret from.
markdownDescription: |-
***
[_Online Documentation_][00]
***

Defines the argument for the command that accepts the name of a specific vault to
retrieve a secret from. Define this argument as an object with the `vaultArg` property
set to the appropriate argument for the extension command, like `--vault` or
`--vault-name`.

To support retrieving secrets from a specific vault, an extension implementing the
`secret` capability _must_ define a vault input argument exactly once.

[00]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>#vault-input-argument
unevaluatedProperties: false
required:
- vaultArg
properties:
vaultArg:
title: Vault input argument definition
description: >-
Defines the literal string for the argument that accepts the name of the vault to
retrieve a secret from, like `--vault` or `--vault-name`.
markdownDescription: |-
Defines the literal string for the argument that accepts the name of the vault to
retrieve a secret from, like `--vault` or `--vault-name`.
type: string
minLength: 1

# Need to use an allOf with multiple possibilities to capture the requirement to define the secret
# name input argument exactly once and the vault input argument no more than once. Note that
# the YAML language server in VS Code currently doesn't understand the `minContains` and
# `maxContains` keywords, so when defining the extension manifest in YAML with the schema, the
# language server identifies the vault input argument as required. When defining the extension
# manifest in JSON, the language server correctly identifies the minimum and maximum number of
# arguments to use.
#
# Unfortunately, because we only get one error message per item in the `allOf` keyword, we have to
# define two entries for the secret name input argument to provide better validation error messages.
#
# We use long lines for error messages, which can't use Markdown, so line breaks are literal.
allOf:
- title: Missing secret name input argument
$comment: >-
This validation subschema ensures that `secret.args` contains a secret name input argument.
Without this argument, DSC can't pass the name of the secret to retrieve when invoking the
extension's secret command.

We define this separately from the maximum contains validation subschema to improve the error
messaging in VS Code.
properties:
args:
errorMessage: |-
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.

You must define one argument in `secret.args` as a JSON object with the `nameArg` property. For more information, see:

<DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>
contains:
type: object
required: [nameArg]
minContains: 1
- title: Multiple secret name input arguments
$comment: >-
This validation subschema ensures that `secret.args` doesn't contain more than one secret
name input argument. Defining more than one secret name input argument is invalid.

We define this separately from the minimum contains validation subschema to improve the error
messaging in VS Code.
properties:
args:
errorMessage: |-
The `secret` command defines the secret name input argument more than once. You can only define one secret name input argument for the command.

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:

<DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>
contains:
type: object
required: [nameArg]
maxContains: 1
- title: Multiple vault input arguments
$comment: >-
This validation subschema ensures that `secret.args` contains zero or one vault input
arguments. Defining a vault input argument is optional. Extensions that don't define the
vault input argument don't support retrieving secrets from a specific vault. Defining
more than one vault input argument is invalid.
properties:
args:
errorMessage: |-
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.

You can define one argument in `secret.args` as a JSON object with the `vaultArg` property. For more information, see:

<DOCS_BASE_URL>/reference/schemas/extension/manifest/secret?<DOCS_VERSION_PIN>
contains:
type: object
required: [vaultArg]
minContains: 0
maxContains: 1

defaultSnippets: # VS Code only
- label: ' Define with arguments (static string and secret name input arguments)'
markdownDescription: |-
Define the `secret` command where the secret name is passed to the command. Use this
snippet if the extension doesn't support retrieving a secret from a specific vault.
body:
executable: ${1:executable_name}
args:
- ${2:get}
- nameArg: ${3:--secret-name}
- label: ' Define with arguments (static string, secret name, and vault input arguments)'
markdownDescription: |-
Define the `secret` command where both the secret name and the vault name are passed to the
command. Use this snippet if the extension supports retrieving a secret from a specific vault.
body:
executable: ${1:executable_name}
args:
- ${2:get}
- nameArg: ${3:--secret-name}
- vaultArg: ${4:--vault-name}
2 changes: 2 additions & 0 deletions schemas/src/extension/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ properties:
characters are permitted.
discover:
$ref: /<PREFIX>/<VERSION>/extension/manifest.discover.yaml
secret:
$ref: /<PREFIX>/<VERSION>/extension/manifest.secret.yaml
exitCodes:
# This setting in the root of the schema implies exit codes must have the
# same meaning across all executions. What about implementations that
Expand Down
49 changes: 49 additions & 0 deletions schemas/src/extension/stdout/secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
$schema: https://json-schema.org/draft/2020-12/schema
$id: <HOST>/<PREFIX>/<VERSION>/extension/stdout/secret.yaml

title: Secret extension operation stdout
description: >-
Represents the secret text returned by a DSC extension for a secret request.
markdownDescription: | # VS Code only
***
[_Online Documentation_][00]
***

Represents the secret text returned by a DSC extension for a secret request. The output that
DSC expects from the extension depends on the result of retrieving the secret:

- If the extension successfully retrieves the secret, DSC expects the extension to:

1. Emit a single line containing only the secret value, not wrapped in quotation marks or a
JSON object, to stdout. DSC treats the literal text returned by the extension as the secret
value. If the emitted output contains any newlines, the output is invalid.
1. Exit with code `0` to indicate success.

- If the extension can't retrieve the secret because no secret with the given name was
discovered, DSC expects the extension to:

1. Emit _no output_ to stdout. If the extension emits any output to stdout, DSC interprets the
emitted text to be the secret value.
1. Optionally emit messages to stderr as [JSON Lines][01] to indicate the attempt to retrieve
the secret and noting that the secret doesn't exist. DSC surfaces emitted messages to the
caller depending on the message level.
1. Exit with code `0` to indicate success. For DSC, an extension not returning a secret value
because no secret with the given name exists is _not_ a failure.

- If the extension can't retrieve the secret for any other reason, such as requiring the vault
to be unlocked or encountering an API error, DSC expects the extension to:

1. Emit _no output_ to stdout.
1. Optionally emit detailed error messages to stderr as [JSON Lines][01] to provide contextual
information about how and why the retrieval failed. DSC always surfaces emitted error
messages to the caller.
1. Exit with a nonzero code to indicate failure. If the extension manifest defines the
[`exitCodes`][02] field, DSC surfaces the meaning of the exit code to the caller.

[00]: <DOCS_BASE_URL>/reference/schemas/extension/stdout/secret?<DOCS_VERSION_PIN>
[01]: https://jsonlines.org/
[02]: <DOCS_BASE_URL>/reference/schemas/extension/manifest/root?<DOCS_VERSION_PIN>#exitcodes

type: string
minLength: 0
Loading