PowerShell / DSC

This repo is for the DSC v3 project
MIT License
135 stars 22 forks source link

DSC Resource declared in `yaml` not discovered if `schema:` is present and not working if `schema:` is absent. #366

Open joshcorr opened 3 months ago

joshcorr commented 3 months ago

Prerequisites

Steps to reproduce

In PR #311 support for definitions of resources in YAML was added. However, this does not appear to be working as expected for the schema portion of the definition.

Using a working json schema for a resource (see below), I run y2j to convert it to yaml (also seen below), but the resource is not discovered when dsc resource list is executed. If I remove the schema: section at the end of the yaml file (as this example suggests is not necessary) the resource is discovered but does not execute as the schema is missing from the resource. As I am using the yaml language server validation, any modification to the schema section to support a YAML tag does not appear valid.

working Json resource definition

{
  "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/bundled/resource/manifest.json",
  "type": "joshcorr/test",
  "version": "0.1.0",
  "description": "Synopsis for the resource's purpose",
  "get": {
    "executable": "powershell",
    "args": [
      "-NoLogo",
      "-NonInteractive",
      "-NoProfile",
      "-Command",
      "$value = ($Input | ConvertFrom-Json).text;",
      "@{\"text\"=\"$value\"} | Convertto-json -Compress"
    ],
    "input": "stdin"
  },
  "set": {
    "executable": "powershell",
    "args": [
      "-NoLogo",
      "-NonInteractive",
      "-NoProfile",
      "-Command",
      "$value = ($Input | ConvertFrom-Json).text;",
      "@{\"text\"=\"$value set\"} | Convertto-json -Compress"
    ],
    "input": "stdin",
    "implementsPretest": false,
    "return": "state"
  },
  "schema": {
    "embedded": {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type": "object",
      "properties": {
        "name": {
          "title": "text",
          "description": "Defines the text you want to output",
          "type": "string"
        }
      }
    }
  }
}

non discoverable yaml resource definition

# yaml-language-server: $schema=https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/bundled/resource/manifest.vscode.json
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/bundled/resource/manifest.json
type: joshcorr/test
version: 0.1.0
description: Synopsis for the resource's purpose
get:
  executable: powershell
  args:
  - -NoLogo
  - -NonInteractive
  - -NoProfile
  - -Command
  - $value = ($Input | ConvertFrom-Json).text;
  - '@{"text"="$value"} | Convertto-json -Compress'
  input: stdin
set:
  executable: powershell
  args:
  - -NoLogo
  - -NonInteractive
  - -NoProfile
  - -Command
  - $value = ($Input | ConvertFrom-Json).text;
  - '@{"text"="$value set"} | Convertto-json -Compress'
  input: stdin
  implementsPretest: false
  return: state
schema:
  embedded:
    $schema: http://json-schema.org/draft-07/schema#
    type: object
    properties:
      name:
        title: text
        description: Defines the text you want to output
        type: string

discoverable but non-working yaml resource definition

# yaml-language-server: $schema=https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/bundled/resource/manifest.vscode.json
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/bundled/resource/manifest.json
type: joshcorr/test
version: 0.1.0
description: Synopsis for the resource's purpose
get:
  executable: powershell
  args:
  - -NoLogo
  - -NonInteractive
  - -NoProfile
  - -Command
  - $value = ($Input | ConvertFrom-Json).text;
  - '@{"text"="$value"} | Convertto-json -Compress'
  input: stdin
set:
  executable: powershell
  args:
  - -NoLogo
  - -NonInteractive
  - -NoProfile
  - -Command
  - $value = ($Input | ConvertFrom-Json).text;
  - '@{"text"="$value set"} | Convertto-json -Compress'
  input: stdin
  implementsPretest: false
  return: state

Expected behavior

dsc resource list joshcorr/test

Type           Version  Methods   Requires  Description
-------------------------------------------------------------------------------
joshcorr/test  0.1.0    get, set            Synopsis for the resource's purpose

when executing the resource (that may be discovered)

(@{"text" = "something2" }  | ConvertTo-Json) | dsc resource set -r joshcorr/test
beforeState:
  text: something2
afterState:
  text: something2 set
changedProperties:
- text

Actual behavior

dsc resource list joshcorr/test
2024-03-17T21:56:25.513921Z  WARN dsc_lib::discovery::command_discovery: 64: Manifest: C:\Users\User\.dsc\resources\test.dsc.resource.yaml
YAML: schema: invalid type: map, expected a YAML tag starting with '!' at line 28 column 3

Type  Version  Methods  Requires  Description
---------------------------------------------

when executing the resource (that may be discovered)

(@{"text" = "something2" }  | ConvertTo-Json) | dsc resource set -r joshcorr/test
2024-03-17T22:08:17.123920Z ERROR dsc::resource_command: 139: Error: Validation: Schema not available

Error details

No response

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.1
PSEdition                      Core
GitCommitId                    7.4.1
OS                             Microsoft Windows 10.0.22621
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Version

dsc 3.0.0-alpha.5

Visuals

No response

michaeltlombardi commented 3 months ago

@joshcorr, the implication in the example document that the schema keyword isn't required is a mistake on my part. I originally created that example to show how users and authors could edit the data files with IntelliSense and validation provided by the JSON Schemas. I'll update that example shortly to be more correct, I can see how the missing key implies that it's not required.

I did some testing, and the following definition is discoverable and functional, but fails our schema validation:

$schema:     https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/bundled/resource/manifest.vscode.json
type:        Example/Foo
version:     0.1.0
description: An example resource to manage the foo application.
tags:
  - foo
get:
  executable:  foo
  args:       [get]
  input:      stdin
set:
  executable: foo
  input:      stdin
  preTest:    false
  return:     state
schema:
  !command
  executable:  foo
  args:       [schema]

Note that for DSC to correctly parse the data from YAML, you have to use the !<tag> syntax, whereas in JSON you embed the data by the <tag> keyword - in this case, command:

{
  // ... rest of the manifest
  "schema": {
    "command": { "executable": "foo", "args": ["schema"] }
  }
}
# ... rest of the manifest
schema:
  !command
  executable:  foo
  args:       [schema]

This is non-intuitive, and difficult to represent with a shared schema (and VS Code will complain that !command, !url, and !embedded are unknown tags).

I believe the fix is to use the serde_yaml::with::singleton_map customization - the example seem to show the behavior we want.