Skip to content

Conversation

@thockin
Copy link

@thockin thockin commented Jun 10, 2025

KEP kubernetes/enhancements#5295

KYAML is a strict subset of YAML, which is sort of halfway between YAML and JSON. It has the following properties:

  • Does not depend on whitespace (easier to text-patch and template).
  • Always quotes value strings (no ambiguity aroud things like "no").
  • Allows quoted keys, but does not require them, and only quotes them if they are not obviously safe (e.g. "no" would always be quoted).
  • Always uses {} for structs and maps (no more obscure errors about mapping values).
  • Always uses [] for lists (no more trying to figure out if a dash changes the meaning).
  • When printing, it includes a header which makes it clear this is YAML and not ill-formed JSON.
  • Allows trailing commas
  • Allows comments,
  • Tries to economize on vertical space by "cuddling" some kinds of brackets together.
  • Retains comments.

Output from kubectl get -o kyaml can be fed back into kubectl.

Examples:

A struct:

metadata: {
  creationTimestamp: "2024-12-11T00:10:11Z",
  labels: {
    app: "hostnames",
  },
  name: "hostnames",
  namespace: "default",
  resourceVersion: "15231643",
  uid: "f64dbcba-9c58-40b0-bbe7-70495efb5202",
}

A list of primitves:

ipFamilies: [
  "IPv4",
  "IPv6",
]

A list of structs:

ports: [{
  port: 80,
  protocol: "TCP",
  targetPort: 80,
}, {
  port: 443,
  protocol: "TCP",
  targetPort: 443,
}]

A multi-document stream:

---
{
  foo: "bar",
}
---
{
  qux: "zrb",
}

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Jun 10, 2025
@k8s-ci-robot k8s-ci-robot requested a review from deads2k June 10, 2025 04:12
@k8s-ci-robot k8s-ci-robot added the sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. label Jun 10, 2025
@k8s-ci-robot k8s-ci-robot requested a review from jpbetz June 10, 2025 04:12
@k8s-ci-robot k8s-ci-robot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Jun 10, 2025
@thockin thockin changed the title Add "kyaml" support Add "kyaml" support and yamlfmt Jun 10, 2025
@thockin
Copy link
Author

thockin commented Jun 10, 2025

@thockin thockin force-pushed the master branch 2 times, most recently from c72348b to 8fb29f1 Compare June 10, 2025 04:50
Copy link
Member

@BenTheElder BenTheElder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quick pass just on go.mod and yamlfmt/

}
}

func renderYAML(in io.Reader, format string, printDiff bool, out io.Writer) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks ripe for a unit test?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, fair. I didn't write tests for the CLI tool yet.

@thockin
Copy link
Author

thockin commented Jun 10, 2025

Pushed with everything but a unit test.

Copy link

@liggitt liggitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super quick sweep with a couple initial comments, didn't dig very deep into the impl

help := flag.Bool("?", false, "print usage and exit")
diff := flag.Bool("d", false, "diff input files with their formatted versions")
write := flag.Bool("w", false, "write result to input files instead of stdout")
format := flag.String("o", "yaml", "output format: may be 'yaml' or 'kyaml'")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised to even offer a non-kyaml option here, let alone defaulting to it... don't we want to be opinionated towards kyaml?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mayber eventually but I don't KNOW that yet? There's enough pushback that I think we should be cautious and open to being wrong.

kyaml/kyaml.go Outdated
if lazyQuote && !needsQuotes(val) {
fmt.Fprint(out, val)
} else {
fmt.Fprint(out, strconv.Quote(val))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is standard go quoting 100% compatible with yaml string escaping?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair question. I will have to evaluate that and get back

Copy link
Member

@BenTheElder BenTheElder Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://yaml.org/spec/1.2.2/#escaped-characters

All non-printable characters must be escaped. YAML escape sequences use the “\” notation common to most modern computer languages [...]

[...]

YAML escape sequences are a superset of C’s escape sequences:

https://pkg.go.dev/strconv#Quote

Quote returns a double-quoted Go string literal representing s. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for control characters and non-printable characters as defined by IsPrint.

https://pkg.go.dev/strconv#IsPrint

IsPrint reports whether the rune is defined as printable by Go, with the same definition as unicode.IsPrint: letters, numbers, punctuation, symbols and ASCII space.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be compatible.

\t \n, \xFF \u0100 escape style are all in "Example 5.13 Escaped Characters" in YAML 1.2.2 spec.

I also see these in 1.1 "Example 5.14. Escaped Characters" and even the 1.0 draft "4.6.9. Escaping".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I read the YAML spec correctly, '\x00' should escape in YAML as "\0" but Go converts it to "\x00" - so I probably have to decode manually.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I have no idea how to read that table - it includes space and slash, which clearly are not actually escaped

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I read the YAML spec correctly, '\x00' should escape in YAML as "\0" but Go converts it to "\x00" - so I probably have to decode manually.

I don't think \x00 is invalid in yaml, but \0 would probably be canonical. https://go.dev/play/p/a6_gobBEp0k

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a commit, but not sure we need it.

It's unclear if we actually need to change this logic. The round-trip test works without it, but we could very likely just be making the same bad assumptions as go-yaml.

The spec describes escapes for things like space and forward-slash, which seem wrong to escape, so I may be misreading it.

@thockin
Copy link
Author

thockin commented Jun 10, 2025

Easy things fixed

@thockin thockin force-pushed the master branch 3 times, most recently from c603bd5 to 17c7943 Compare June 10, 2025 22:07
@thockin thockin force-pushed the master branch 2 times, most recently from d46fbd4 to 0a0c08a Compare June 12, 2025 23:22
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 25, 2025
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 25, 2025
@thockin
Copy link
Author

thockin commented Jun 25, 2025

TTBOMK this is ready for review.

@thockin
Copy link
Author

thockin commented Jun 30, 2025

I have a commit cooking to simplify tests, but the rest of this is "done".

@thockin
Copy link
Author

thockin commented Jul 1, 2025

Tests are a bit simpler.

KYAML is a strict subset of YAML, which is sort of halfway between YAML
and JSON.  It has the following properties:
* Does not depend on whitespace (easier to text-patch and template).
* Always quotes value strings (no ambiguity aroud things like "no").
* Allows quoted keys, but does not require them, and only quotes them if
  they are not obviously safe (e.g. "no" would always be quoted).
* Always uses {} for structs and maps (no more obscure errors about
  mapping values).
* Always uses [] for lists (no more trying to figure out if a dash
  changes the meaning).
* When printing, it includes a header which makes it clear this is YAML
  and not ill-formed JSON.
* Allows trailing commas
* Allows comments,
* Tries to economize on vertical space by "cuddling" some kinds of
  brackets together.
* Retains comments.

Examples:

A struct:

```yaml
metadata: {
  creationTimestamp: "2024-12-11T00:10:11Z",
  labels: {
    app: "hostnames",
  },
  name: "hostnames",
  namespace: "default",
  resourceVersion: "15231643",
  uid: "f64dbcba-9c58-40b0-bbe7-70495efb5202",
}
```

A list of primitves:

```yaml
ipFamilies: [
  "IPv4",
  "IPv6",
]
```

A list of structs:

```yaml
ports: [{
  port: 80,
  protocol: "TCP",
  targetPort: 80,
}, {
  port: 443,
  protocol: "TCP",
  targetPort: 443,
}]
```

A multi-document stream:

```yaml
---
{
  foo: "bar",
}
---
{
  qux: "zrb",
}
```
thockin added 6 commits July 14, 2025 11:40
* Can read 1 file (cmdline)
* Can read multiple files (cmdline)
* Can read stdin
* Can write traditional YAML or KYAML
* Can diff input vs output (-d)
* Can write results to the input files (-w)
It's unclear if we actually need this.  The round-trip test works
without it.

The spec describes escapes for things like space and forward-slash,
which seem wrong to escape, so I may be misreading it.
Merge the "regular" and "compact" tests together.
@thockin
Copy link
Author

thockin commented Jul 14, 2025

a k/k PR preview: kubernetes/kubernetes#132942

@thockin
Copy link
Author

thockin commented Jul 18, 2025

FTR: I will be away for the last 3 days of the code-freeze, so if we want this in, I need it soon. Otherwise we can push to next release.

@jenshu jenshu mentioned this pull request Jul 19, 2025
15 tasks
Copy link
Member

@BenTheElder BenTheElder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 21, 2025
Copy link

@soltysh soltysh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry was out, but this lgtm

}
if indent != 0 {
return fmt.Errorf("kyaml internal error: line %d: document non-zero indent (%d)", doc.Line, indent)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: some of the error cases are not covered by tests, would be nice to get that added in followup PRs.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the error paths are more like "assert" - they are no expected to ever trigger, but when they do it will at least fail in a semi-obvious way. Will look at adding test cases. :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some tests around error handling. Coverage is > 89%. What's left is mostly error-returning from YAML and JSON parsing, and some impossible-to-reach (for now?) comment handling (yaml.v3 comments are...inconsistently implemented)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 24, 2025
@thockin
Copy link
Author

thockin commented Jul 24, 2025

Pushed with new test cases for corner cases mostly. Needs LGTM and approve.

@jpbetz
Copy link

jpbetz commented Jul 24, 2025

/lgtm
/approve

Expanded test coverage looks excellent

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 24, 2025
@k8s-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jpbetz, soltysh, thockin

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 24, 2025
@k8s-ci-robot k8s-ci-robot merged commit 048d724 into kubernetes-sigs:master Jul 24, 2025
6 checks passed
@thockin
Copy link
Author

thockin commented Jul 24, 2025

This repo seems to use tags - do we need a v1.5.1 or v1.6.0 tag so I can re-vendor to k/k ?

@liggitt
Copy link

liggitt commented Jul 24, 2025

yes, let's add v1.6.0 since this added features

@liggitt
Copy link

liggitt commented Jul 24, 2025

https://github.com/kubernetes-sigs/yaml/releases/tag/v1.6.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants