Merge pull request #132942 from thockin/kyaml

Add KYAML support to kubectl
This commit is contained in:
Kubernetes Prow Robot
2025-07-24 17:59:06 -07:00
committed by GitHub
70 changed files with 1064 additions and 240 deletions

View File

@@ -1,10 +0,0 @@
# See the OWNERS docs at https://go.k8s.io/owners
reviewers:
- bentheelder
- dims
approvers:
- dims
labels:
- sig/testing
- sig/contributor-experience

View File

@@ -1,72 +0,0 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"flag"
"fmt"
"io"
"os"
yaml "go.yaml.in/yaml/v3"
)
func main() {
indent := flag.Int("indent", 2, "default indent")
flag.Parse()
for _, path := range flag.Args() {
sourceYaml, err := os.ReadFile(path)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
continue
}
rootNode, err := fetchYaml(sourceYaml)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
continue
}
writer, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
continue
}
err = streamYaml(writer, indent, rootNode)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
continue
}
}
}
func fetchYaml(sourceYaml []byte) (*yaml.Node, error) {
rootNode := yaml.Node{}
err := yaml.Unmarshal(sourceYaml, &rootNode)
if err != nil {
return nil, err
}
return &rootNode, nil
}
func streamYaml(writer io.Writer, indent *int, in *yaml.Node) error {
encoder := yaml.NewEncoder(writer)
encoder.SetIndent(*indent)
err := encoder.Encode(in)
if err != nil {
return err
}
return encoder.Close()
}

View File

@@ -1,53 +0,0 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"bufio"
"bytes"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFetchYaml(t *testing.T) {
sourceYaml := ` # See the OWNERS docs at https://go.k8s.io/owners
approvers:
- dep-approvers
- thockin # Network
- liggitt
labels:
- sig/architecture
`
outputYaml := `# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- dep-approvers
- thockin # Network
- liggitt
labels:
- sig/architecture
`
node, _ := fetchYaml([]byte(sourceYaml))
var output bytes.Buffer
indent := 2
writer := bufio.NewWriter(&output)
_ = streamYaml(writer, &indent, node)
_ = writer.Flush()
assert.Equal(t, outputYaml, output.String(), "yaml was not formatted correctly")
}

4
go.mod
View File

@@ -72,7 +72,6 @@ require (
go.uber.org/goleak v1.3.0
go.uber.org/zap v1.27.0
go.yaml.in/yaml/v2 v2.4.2
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/crypto v0.36.0
golang.org/x/net v0.38.0
golang.org/x/oauth2 v0.27.0
@@ -122,7 +121,7 @@ require (
sigs.k8s.io/knftables v0.0.17
sigs.k8s.io/randfill v1.0.0
sigs.k8s.io/structured-merge-diff/v6 v6.3.0
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/yaml v1.6.0
)
require (
@@ -208,6 +207,7 @@ require (
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/text v0.23.0 // indirect

4
go.sum
View File

@@ -501,5 +501,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -18,6 +18,7 @@ require (
gotest.tools/gotestsum v1.12.0
honnef.co/go/tools v0.6.1
k8s.io/publishing-bot v0.5.0
sigs.k8s.io/yaml v1.6.0
)
require (
@@ -51,6 +52,7 @@ require (
github.com/spf13/viper v1.20.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v3 v3.0.3 // indirect
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect

View File

@@ -115,6 +115,10 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -242,3 +246,7 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/publishing-bot v0.5.0 h1:Hfnhltr+khEcqvoK4GBYrtaA8dHJ50Xjyi+0KGUfU3I=
k8s.io/publishing-bot v0.5.0/go.mod h1:S5+zQQhsVUEqdcaohbYf8O+2BeeWRtuYzp4tQLr5An8=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -45,4 +45,7 @@ import (
// protobuf generation
_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
// yamlfmt
_ "sigs.k8s.io/yaml/yamlfmt"
)

View File

@@ -25,16 +25,12 @@ kube::golang::setup_env
cd "${KUBE_ROOT}"
find_files() {
find . -not \( \
\( \
-wholename './.git' \
-o -wholename './_output' \
-o -wholename './release' \
-o -wholename './target' \
-o -wholename '*/vendor/*' \
\) -prune \
\) -name 'OWNERS*'
find_owners_files() {
git ls-files \
'OWNERS*' \
'**/OWNERS*' \
':!:vendor/*/OWNERS*'
}
find_files | xargs go run cmd/yamlfmt/yamlfmt.go
go -C "${KUBE_ROOT}/hack/tools" install sigs.k8s.io/yaml/yamlfmt
find_owners_files | xargs yamlfmt -o yaml -w

View File

@@ -314,9 +314,9 @@ options:
# make root approval non-recursive
no_parent_owners: true
approvers:
- dep-approvers
- dep-approvers
reviewers:
- dep-reviewers
- dep-reviewers
__EOF__
# === Disallow transitive dependencies on k8s.io/kubernetes

View File

@@ -15,9 +15,9 @@
# limitations under the License.
# This script checks whether the OWNERS files need to be formatted or not by
# `yamlfmt`. Run `hack/update-yamlfmt.sh` to actually format sources.
# `yamlfmt`. Run `hack/update-owners-fmt.sh` to actually format sources.
#
# Usage: `hack/verify-yamlfmt.sh`.
# Usage: `hack/verify-owners-fmt.sh`.
set -o errexit
@@ -27,4 +27,4 @@ set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
source "${KUBE_ROOT}/hack/lib/verify-generated.sh"
kube::verify::generated "YAML files need to be formatted" "Please run 'hack/update-yamlfmt.sh'" hack/update-yamlfmt.sh
kube::verify::generated "YAML files need to be formatted" "Please run 'hack/update-owners-fmt.sh'" hack/update-owners-fmt.sh

View File

@@ -32,7 +32,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace k8s.io/apimachinery => ../apimachinery

View File

@@ -117,5 +117,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -38,7 +38,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
sigs.k8s.io/randfill v1.0.0
sigs.k8s.io/structured-merge-diff/v6 v6.3.0
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/yaml v1.6.0
)
require (

View File

@@ -345,5 +345,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -29,7 +29,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
sigs.k8s.io/randfill v1.0.0
sigs.k8s.io/structured-merge-diff/v6 v6.3.0
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/yaml v1.6.0
)
require (

View File

@@ -152,5 +152,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -60,7 +60,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
sigs.k8s.io/randfill v1.0.0
sigs.k8s.io/structured-merge-diff/v6 v6.3.0
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/yaml v1.6.0
)
require (

View File

@@ -345,5 +345,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -27,7 +27,7 @@ require (
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
sigs.k8s.io/kustomize/api v0.20.1
sigs.k8s.io/kustomize/kyaml v0.20.1
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/yaml v1.6.0
)
require (

View File

@@ -196,5 +196,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -17,6 +17,8 @@ limitations under the License.
package genericclioptions
import (
"os"
"slices"
"strings"
"github.com/spf13/cobra"
@@ -29,7 +31,12 @@ func (f *JSONYamlPrintFlags) AllowedFormats() []string {
if f == nil {
return []string{}
}
return []string{"json", "yaml"}
formats := []string{"json", "yaml"}
// We can't use the cmdutil pkg directly because of import cycle.
if strings.ToLower(os.Getenv("KUBECTL_KYAML")) == "true" {
formats = append(formats, "kyaml")
}
return formats
}
// JSONYamlPrintFlags provides default flags necessary for json/yaml printing.
@@ -47,11 +54,19 @@ func (f *JSONYamlPrintFlags) ToPrinter(outputFormat string) (printers.ResourcePr
var printer printers.ResourcePrinter
outputFormat = strings.ToLower(outputFormat)
valid := f.AllowedFormats()
if !slices.Contains(valid, outputFormat) {
return nil, NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: valid}
}
switch outputFormat {
case "json":
printer = &printers.JSONPrinter{}
case "yaml":
printer = &printers.YAMLPrinter{}
case "kyaml":
printer = &printers.KYAMLPrinter{}
default:
return nil, NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: f.AllowedFormats()}
}

View File

@@ -105,8 +105,9 @@ func TestPrintersSuccess(t *testing.T) {
om := func(name string) metav1.ObjectMeta { return metav1.ObjectMeta{Name: name} }
genericPrinters := map[string]ResourcePrinter{
"json": NewTypeSetter(scheme.Scheme).ToPrinter(&JSONPrinter{}),
"yaml": NewTypeSetter(scheme.Scheme).ToPrinter(&YAMLPrinter{}),
"json": NewTypeSetter(scheme.Scheme).ToPrinter(&JSONPrinter{}),
"yaml": NewTypeSetter(scheme.Scheme).ToPrinter(&YAMLPrinter{}),
"kyaml": NewTypeSetter(scheme.Scheme).ToPrinter(&KYAMLPrinter{}),
}
objects := map[string]runtime.Object{
"pod": &v1.Pod{ObjectMeta: om("pod")},

View File

@@ -0,0 +1,66 @@
/*
Copyright 2025 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// package kyaml provides a printer for Kubernetes objects that formats them
// as KYAML, a strict subset of YAML that is designed to be explicit and
// unambiguous. KYAML is YAML.
package printers
import (
"bytes"
"errors"
"fmt"
"io"
"reflect"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/yaml/kyaml"
)
// KYAMLPrinter is an implementation of ResourcePrinter which formats data into
// a specific dialect of YAML, known as KYAML. KYAML is halfway between YAML
// and JSON, but is a strict subset of YAML, and so it should should be
// readable by any YAML parser. It is designed to be explicit and unambiguous,
// and eschews significant whitespace.
type KYAMLPrinter struct {
encoder kyaml.Encoder
}
// PrintObj prints the data as KYAML to the specified writer.
func (p *KYAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
// We use reflect.Indirect here in order to obtain the actual value from a pointer.
// We need an actual value in order to retrieve the package path for an object.
// Using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
return errors.New(InternalObjectPrinterErr)
}
switch obj := obj.(type) {
case *metav1.WatchEvent:
if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj.Object.Object)).Type().PkgPath()) {
return errors.New(InternalObjectPrinterErr)
}
case *runtime.Unknown:
return p.encoder.FromYAML(bytes.NewReader(obj.Raw), w)
}
if obj.GetObjectKind().GroupVersionKind().Empty() {
return fmt.Errorf("missing apiVersion or kind; try GetObjectKind().SetGroupVersionKind() if you know the type")
}
return p.encoder.FromObject(obj, w)
}

View File

@@ -0,0 +1,33 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"testing"
"sigs.k8s.io/yaml"
"k8s.io/client-go/kubernetes/scheme"
)
func TestKYAMLPrinter(t *testing.T) {
testPrinter(t, NewTypeSetter(scheme.Scheme).ToPrinter(&KYAMLPrinter{}), kyamlUnmarshal)
}
func kyamlUnmarshal(data []byte, v interface{}) error {
return yaml.Unmarshal(data, v)
}

View File

@@ -33,7 +33,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
sigs.k8s.io/randfill v1.0.0
sigs.k8s.io/structured-merge-diff/v6 v6.3.0
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/yaml v1.6.0
)
require (

View File

@@ -173,5 +173,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -109,7 +109,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -322,5 +322,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -115,5 +115,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -51,7 +51,7 @@ require (
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -149,5 +149,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -152,5 +152,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -84,7 +84,7 @@ require (
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -257,5 +257,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -52,7 +52,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -162,5 +162,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -101,7 +101,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -317,5 +317,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -243,5 +243,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -113,5 +113,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -81,7 +81,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -278,5 +278,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -64,7 +64,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -212,5 +212,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -111,7 +111,7 @@ require (
k8s.io/kms v0.0.0 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -324,5 +324,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -168,5 +168,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -46,7 +46,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -172,5 +172,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -13,7 +13,7 @@ require (
k8s.io/client-go v0.0.0
k8s.io/component-base v0.0.0
k8s.io/klog/v2 v2.130.1
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/yaml v1.6.0
)
require (

View File

@@ -192,5 +192,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -44,7 +44,7 @@ require (
sigs.k8s.io/kustomize/kyaml v0.20.1
sigs.k8s.io/randfill v1.0.0
sigs.k8s.io/structured-merge-diff/v6 v6.3.0
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/yaml v1.6.0
)
require (

View File

@@ -253,5 +253,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -455,6 +455,12 @@ const (
// Transition to WebSockets.
RemoteCommandWebsockets FeatureGate = "KUBECTL_REMOTE_COMMAND_WEBSOCKETS"
PortForwardWebsockets FeatureGate = "KUBECTL_PORT_FORWARD_WEBSOCKETS"
// owner: @thockin
// kep: https://kep.k8s.io/5296
//
// Support KYAML output.
KYAMLOutput FeatureGate = "KUBECTL_KYAML"
)
// IsEnabled returns true iff environment variable is set to true.

View File

@@ -62,7 +62,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -253,5 +253,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -58,7 +58,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -166,5 +166,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -19,7 +19,7 @@ require (
k8s.io/component-base v0.0.0
k8s.io/klog/v2 v2.130.1
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
sigs.k8s.io/yaml v1.5.0
sigs.k8s.io/yaml v1.6.0
)
require (

View File

@@ -317,5 +317,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -108,7 +108,7 @@ require (
k8s.io/kms v0.0.0 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -321,5 +321,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -66,7 +66,7 @@ require (
sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -196,5 +196,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -57,7 +57,7 @@ require (
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace (

View File

@@ -167,5 +167,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

4
vendor/OWNERS vendored
View File

@@ -4,6 +4,6 @@ options:
# make root approval non-recursive
no_parent_owners: true
approvers:
- dep-approvers
- dep-approvers
reviewers:
- dep-reviewers
- dep-reviewers

3
vendor/modules.txt vendored
View File

@@ -1285,6 +1285,7 @@ sigs.k8s.io/structured-merge-diff/v6/merge
sigs.k8s.io/structured-merge-diff/v6/schema
sigs.k8s.io/structured-merge-diff/v6/typed
sigs.k8s.io/structured-merge-diff/v6/value
# sigs.k8s.io/yaml v1.5.0
# sigs.k8s.io/yaml v1.6.0
## explicit; go 1.22
sigs.k8s.io/yaml
sigs.k8s.io/yaml/kyaml

828
vendor/sigs.k8s.io/yaml/kyaml/kyaml.go generated vendored Normal file
View File

@@ -0,0 +1,828 @@
/*
Copyright 2025 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package kyaml provides an encoder for KYAML, a strict subset of YAML that is
// designed to be explicit and unambiguous. KYAML is YAML, so any YAML parser
// should be able to read it.
//
// KYAML is designed to be halfway between YAML and JSON, with the following
// properties:
// - Not whitespace-sensitive
// - Allows comments
// - Allows trailing commas
// - Does not require quoted keys
//
// KYAML is an output format, and will follow these conventions:
// - Always double-quote strings, even if they are not ambiguous.
// - Only quote keys that might be ambiguously interpreted (e.g. "no" is
// always quoted).
// - Always use `{}` for structs and maps, and `[]` for lists.
// - Economize on vertical space by cuddling some kinds of brackets together.
// - Render multi-line strings with YAML's line folding, which is close to
// the Go string literal format.
//
// KYAML also includes a document-separator "header" (still valid YAML), which
// helps to disambiguate a KYAML document from an ill-formed JSON document.
//
// Because KYAML is YAML, a KYAML multi-document is a YAML multi-document.
package kyaml
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"regexp"
"strconv"
"strings"
"time"
"unicode"
"unicode/utf8"
yaml "go.yaml.in/yaml/v3"
)
// Encoder formats objects or YAML data (JSON is valid YAML) into KYAML. KYAML
// is halfway between YAML and JSON, but is a strict subset of YAML, so it
// should should be readable by any YAML parser. It is designed to be explicit
// and unambiguous, and eschews significant whitespace.
type Encoder struct {
// Compact tells the encoder to use compact formatting. This puts all the
// data on one line, with no extra newlines, no comments, and no multi-line
// formatting.
Compact bool
}
// FromYAML renders a KYAML (multi-)document from YAML bytes (JSON is YAML),
// including the KYAML header. The result always has a trailing newline.
func (ky *Encoder) FromYAML(in io.Reader, out io.Writer) error {
// We need a YAML decoder to handle multi-document streams.
dec := yaml.NewDecoder(in)
// Process each document in the stream.
for {
var doc yaml.Node
err := dec.Decode(&doc)
if err == io.EOF {
break
}
if err != nil {
return fmt.Errorf("error decoding: %v", err)
}
if doc.Kind != yaml.DocumentNode {
return fmt.Errorf("kyaml internal error: line %d: expected a document node, got %s", doc.Line, ky.nodeKindString(doc.Kind))
}
// Always emit a document separator, which helps disambiguate between YAML
// and JSON.
if _, err := fmt.Fprintln(out, "---"); err != nil {
return err
}
if err := ky.renderDocument(&doc, 0, ky.flags(), out); err != nil {
return err
}
fmt.Fprintf(out, "\n")
}
return nil
}
// FromObject renders a KYAML document from a Go object, including the KYAML
// header. The result always has a trailing newline.
func (ky *Encoder) FromObject(obj any, out io.Writer) error {
jb, err := json.Marshal(obj)
if err != nil {
return fmt.Errorf("error marshaling to JSON: %v", err)
}
// JSON is YAML.
return ky.FromYAML(bytes.NewReader(jb), out)
}
// Marshal renders a single Go object as KYAML, without the header or trailing
// newline.
func (ky *Encoder) Marshal(obj any) ([]byte, error) {
// Convert the object to JSON bytes to take advantage of all the JSON tag
// handling and things like that.
jb, err := json.Marshal(obj)
if err != nil {
return nil, fmt.Errorf("error marshaling to JSON: %v", err)
}
buf := &bytes.Buffer{}
// JSON is YAML.
if err := ky.fromObjectYAML(bytes.NewReader(jb), buf); err != nil {
return nil, fmt.Errorf("error rendering object: %v", err)
}
return buf.Bytes(), nil
}
func (ky *Encoder) fromObjectYAML(in io.Reader, out io.Writer) error {
yb, err := io.ReadAll(in)
if err != nil {
return err
}
var doc yaml.Node
if err := yaml.Unmarshal(yb, &doc); err != nil {
return fmt.Errorf("error decoding: %v", err)
}
if doc.Kind != yaml.DocumentNode {
return fmt.Errorf("kyaml internal error: line %d: expected document node, got %s", doc.Line, ky.nodeKindString(doc.Kind))
}
if err := ky.renderNode(&doc, 0, ky.flags(), out); err != nil {
return fmt.Errorf("error rendering document: %v", err)
}
return nil
}
// From the YAML spec.
const (
intTag = "!!int"
floatTag = "!!float"
boolTag = "!!bool"
strTag = "!!str"
timestampTag = "!!timestamp"
seqTag = "!!seq"
mapTag = "!!map"
nullTag = "!!null"
binaryTag = "!!binary"
mergeTag = "!!merge"
)
type flagMask uint64
const (
flagsNone flagMask = 0
flagLazyQuote flagMask = 0x01
flagCompact flagMask = 0x02
)
// flags returns a flagMask representing the current encoding options. It can
// be used directly or OR'ed with another mask.
func (ky *Encoder) flags() flagMask {
flags := flagsNone
if ky.Compact {
flags |= flagCompact
}
return flags
}
// renderNode processes a YAML node, calling the appropriate render function
// for its type. Each render function should assume that the output "cursor"
// is positioned at the start of the node and should not emit a final newline.
// If a render function needs to linewrap or indent (e.g. a struct), it should
// assume the indent level is currently correct for the node type itself, and
// may need to indent more.
func (ky *Encoder) renderNode(node *yaml.Node, indent int, flags flagMask, out io.Writer) error {
if node == nil {
return nil
}
switch node.Kind {
case yaml.DocumentNode:
return ky.renderDocument(node, indent, flags, out)
case yaml.ScalarNode:
return ky.renderScalar(node, indent, flags, out)
case yaml.SequenceNode:
return ky.renderSequence(node, indent, flags, out)
case yaml.MappingNode:
return ky.renderMapping(node, indent, flags, out)
case yaml.AliasNode:
return ky.renderAlias(node, indent, flags, out)
}
return fmt.Errorf("kyaml internal error: line %d: unknown node kind %v", node.Line, node.Kind)
}
// renderDocument processes a YAML document node, rendering it to the output.
// This function assumes that the output "cursor" is positioned at the start of
// the document. This does not emit a final newline.
func (ky *Encoder) renderDocument(doc *yaml.Node, indent int, flags flagMask, out io.Writer) error {
if len(doc.Content) == 0 {
return fmt.Errorf("kyaml internal error: line %d: document has no content node (%d)", doc.Line, len(doc.Content))
}
if len(doc.Content) > 1 {
return fmt.Errorf("kyaml internal error: line %d: document has more than one content node (%d)", doc.Line, len(doc.Content))
}
if indent != 0 {
return fmt.Errorf("kyaml internal error: line %d: document non-zero indent (%d)", doc.Line, indent)
}
compact := flags&flagCompact != 0
// For document nodes, the cursor is assumed to be ready to render.
child := doc.Content[0]
if !compact {
if len(doc.HeadComment) > 0 {
ky.renderComments(doc.HeadComment, indent, out)
fmt.Fprint(out, "\n")
}
if len(child.HeadComment) > 0 {
ky.renderComments(child.HeadComment, indent, out)
fmt.Fprint(out, "\n")
}
}
if err := ky.renderNode(child, indent, flags, out); err != nil {
return err
}
if !compact {
if len(child.LineComment) > 0 {
ky.renderComments(" "+child.LineComment, 0, out)
}
if len(child.FootComment) > 0 {
fmt.Fprint(out, "\n")
ky.renderComments(child.FootComment, indent, out)
}
if len(doc.LineComment) > 0 {
fmt.Fprint(out, "\n")
ky.renderComments(" "+doc.LineComment, 0, out)
}
if len(doc.FootComment) > 0 {
fmt.Fprint(out, "\n")
ky.renderComments(doc.FootComment, indent, out)
}
}
return nil
}
// renderScalar processes a YAML scalar node, rendering it to the output. This
// DOES NOT render a trailing newline or head/line/foot comments, as those
// require the parent context.
func (ky *Encoder) renderScalar(node *yaml.Node, indent int, flags flagMask, out io.Writer) error {
switch node.Tag {
case intTag, floatTag, boolTag, nullTag:
fmt.Fprint(out, node.Value)
case strTag, timestampTag:
return ky.renderString(node.Value, indent+1, flags, out)
default:
return fmt.Errorf("kyaml internal error: line %d: unknown tag %q on scalar node %q", node.Line, node.Tag, node.Value)
}
return nil
}
const kyamlFoldStr = "\\\n"
var regularEscapeMap = map[rune]string{
'\n': "\\n" + kyamlFoldStr, // use YAML's line folding to make the output more readable
'\t': "\t", // literal tab
}
var compactEscapeMap = map[rune]string{
'\n': "\\n",
'\t': "\\t",
}
// renderString processes a string (either single-line or multi-line),
// rendering it to the output. This DOES NOT render a trailing newline.
func (ky *Encoder) renderString(val string, indent int, flags flagMask, out io.Writer) error {
lazyQuote := flags&flagLazyQuote != 0
compact := flags&flagCompact != 0
multi := strings.Contains(val, "\n")
if !multi && lazyQuote && !needsQuotes(val) {
fmt.Fprint(out, val)
return nil
}
// Special cases for certain input.
escapeOverrides := regularEscapeMap
if compact {
escapeOverrides = compactEscapeMap
}
//
// The rest of this is borrowed from Go's strconv.Quote implementation.
//
// accumulate into a buffer
buf := &bytes.Buffer{}
// opening quote
fmt.Fprint(buf, `"`)
if multi && !compact {
fmt.Fprint(buf, kyamlFoldStr)
}
// Iterating a string with invalid UTF8 returns RuneError rather than the
// bytes, so we iterate the string and decode the runes. This is a bit
// slower, but gives us a better result.
s := val
for width := 0; len(s) > 0; s = s[width:] {
r := rune(s[0])
width = 1
if r >= utf8.RuneSelf {
r, width = utf8.DecodeRuneInString(s)
}
if width == 1 && r == utf8.RuneError {
fmt.Fprint(buf, `\x`)
fmt.Fprintf(buf, "%02x", s[0])
continue
}
ky.appendEscapedRune(r, indent, escapeOverrides, buf)
}
// closing quote
afterNewline := buf.Bytes()[len(buf.Bytes())-1] == '\n'
if multi && !compact {
if !afterNewline {
fmt.Fprint(buf, kyamlFoldStr)
}
ky.writeIndent(indent, buf)
}
fmt.Fprint(buf, `"`)
fmt.Fprint(out, buf.String())
return nil
}
var allowedUnquotedAnywhere = map[rune]bool{
'_': true,
}
var allowedUnquotedInterior = map[rune]bool{
'-': true,
'.': true,
'/': true,
}
func needsQuotes(s string) bool {
if s == "" {
return true
}
if isTypeAmbiguous(s) {
return true
}
runes := []rune(s)
for i, r := range runes {
if unicode.IsLetter(r) || unicode.IsNumber(r) || allowedUnquotedAnywhere[r] {
continue
}
if i > 0 && i < len(runes)-1 && allowedUnquotedInterior[r] {
continue
}
// it's something we don't explicitly allow
return true
}
return false
}
// From https://yaml.org/type/int.html and https://yaml.org/type/float.html
var sexagesimalRE = regexp.MustCompile(`^[+-]?[1-9][0-9_]*(:[0-5]?[0-9])+(\.[0-9_]*)?$`)
// isTypeAmbiguous returns true if a YAML parser might interpret the unquoted
// form of the string argument as a YAML type other than string (e.g. `true`
// would be interpreted as a boolean).
func isTypeAmbiguous(s string) bool {
// Null-like strings: https://yaml.org/type/null.html
if len(s) <= 5 {
switch strings.ToLower(s) {
case "null", "~", "":
return true
}
}
// Boolean-like strings: https://yaml.org/type/bool.html
if _, err := strconv.ParseBool(s); err == nil {
return true
}
if len(s) <= 5 {
switch strings.ToLower(s) {
case "true", "y", "yes", "on", "false", "n", "no", "off":
return true
}
}
// Number-like strings: https://yaml.org/type/int.html and
// https://yaml.org/type/float.html
//
// NOTE: the stripping of underscores is gross.
sWithoutUnderscores := strings.ReplaceAll(s, "_", "")
// Handles binary ("0b"), octal ("0" or "0o"), decimal, and hex ("0x")
if _, err := strconv.ParseInt(sWithoutUnderscores, 0, 64); err == nil && !isSyntaxError(err) {
return true
}
// Handles standard and scientific notation.
if _, err := strconv.ParseFloat(sWithoutUnderscores, 64); err == nil && !isSyntaxError(err) {
return true
}
// Sexagesimal strings like "11:00" (in YAML 1.1, removed in 1.2):
// https://yaml.org/type/int.html and https://yaml.org/type/float.html
if sexagesimalRE.MatchString(s) {
return true
}
// Infinity and NaN: https://yaml.org/type/float.html
if len(s) <= 5 {
switch strings.ToLower(s) {
case ".inf", "-.inf", "+.inf", ".nan":
return true
}
}
// Time-like strings
if _, matches := parseTimestamp(s); matches {
return true
}
return false
}
func isSyntaxError(err error) bool {
var numerr *strconv.NumError
if ok := errors.As(err, &numerr); ok {
return errors.Is(numerr.Err, strconv.ErrSyntax)
}
return false
}
// This is a subset of the formats allowed by the regular expression
// defined at http://yaml.org/type/timestamp.html.
//
// NOTE: This was copied from go.yaml.in/yaml/v2
var allowedTimestampFormats = []string{
"2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields.
"2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t".
"2006-1-2 15:4:5.999999999", // space separated with no time zone
"2006-1-2", // date only
// Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5"
// from the set of examples.
}
// parseTimestamp parses s as a timestamp string and
// returns the timestamp and reports whether it succeeded.
// Timestamp formats are defined at http://yaml.org/type/timestamp.html
//
// NOTE: This was copied from go.yaml.in/yaml/v2
func parseTimestamp(s string) (time.Time, bool) {
// TODO write code to check all the formats supported by
// http://yaml.org/type/timestamp.html instead of using time.Parse.
// Quick check: all date formats start with YYYY-.
i := 0
for ; i < len(s); i++ {
if c := s[i]; c < '0' || c > '9' {
break
}
}
if i != 4 || i == len(s) || s[i] != '-' {
return time.Time{}, false
}
for _, format := range allowedTimestampFormats {
if t, err := time.Parse(format, s); err == nil {
return t, true
}
}
return time.Time{}, false
}
// We use a buffer here so we can peek backwards.
func (ky *Encoder) appendEscapedRune(r rune, indent int, escapeOverrides map[rune]string, buf *bytes.Buffer) {
afterNewline := buf.Bytes()[len(buf.Bytes())-1] == '\n'
if afterNewline {
ky.writeIndent(indent, buf)
// We want to preserve leading whitespace in the source string, so if
// we find whitespace, we need to escape it. We don't want to
// escape lines without leading whitespace, but we DO want to render
// the result with fidelity to vertical alignment, so we write an extra
// space. This is OK, because all whitespace before the first
// non-whitespace character is dropped, as per YAML spec. If there are
// no lines with leading whitespace it looks like the indent is one too
// many, which seems OK.
if unicode.IsSpace(r) && r != '\n' {
buf.WriteRune('\\')
} else {
buf.WriteRune(' ')
}
}
if s, found := escapeOverrides[r]; found {
buf.WriteString(s)
return
}
if r == '"' || r == '\\' { // always escaped
buf.WriteRune('\\')
buf.WriteRune(r)
return
}
if unicode.IsPrint(r) {
buf.WriteRune(r)
return
}
switch r {
case '\a':
buf.WriteString(`\a`)
case '\b':
buf.WriteString(`\b`)
case '\f':
buf.WriteString(`\f`)
case '\n':
buf.WriteString(`\n`)
case '\r':
buf.WriteString(`\r`)
case '\t':
buf.WriteString(`\t`)
case '\v':
buf.WriteString(`\v`)
case '\x00':
buf.WriteString(`\0`)
case '\x1b':
buf.WriteString(`\e`)
case '\x85':
buf.WriteString(`\N`)
case '\xa0':
buf.WriteString(`\_`)
case '\u2028':
buf.WriteString(`\L`)
case '\u2029':
buf.WriteString(`\P`)
default:
const hexits = "0123456789abcdef"
switch {
case r < ' ' || r == 0x7f:
buf.WriteString(`\x`)
buf.WriteByte(hexits[byte(r)>>4])
buf.WriteByte(hexits[byte(r)&0xF])
case !utf8.ValidRune(r):
r = utf8.RuneError
fallthrough
case r < 0x10000:
buf.WriteString(`\u`)
for s := 12; s >= 0; s -= 4 {
buf.WriteByte(hexits[r>>uint(s)&0xF])
}
default:
buf.WriteString(`\U`)
for s := 28; s >= 0; s -= 4 {
buf.WriteByte(hexits[r>>uint(s)&0xF])
}
}
}
}
// renderSequence processes a YAML sequence node, rendering it to the output. This
// DOES NOT render a trailing newline or head/line/foot comments of the sequence
// itself, but DOES render comments of the child nodes.
func (ky *Encoder) renderSequence(node *yaml.Node, indent int, flags flagMask, out io.Writer) error {
if len(node.Content) == 0 {
fmt.Fprint(out, "[]")
return nil
}
if flags&flagCompact != 0 {
return ky.renderCompactSequence(node, flags, out)
}
// See if this list can use cuddled formatting.
cuddle := true
for _, child := range node.Content {
if !isCuddledKind(child) {
cuddle = false
break
}
if len(child.HeadComment)+len(child.LineComment)+len(child.FootComment) > 0 {
cuddle = false
break
}
}
if cuddle {
return ky.renderCuddledSequence(node, indent, flags, out)
}
return ky.renderUncuddledSequence(node, indent, flags, out)
}
// renderCompactSequence renders a YAML sequence node in compact form.
func (ky *Encoder) renderCompactSequence(node *yaml.Node, flags flagMask, out io.Writer) error {
fmt.Fprint(out, "[")
for i, child := range node.Content {
if i > 0 {
fmt.Fprint(out, ", ")
}
if err := ky.renderNode(child, 0, flags, out); err != nil {
return err
}
}
fmt.Fprint(out, "]")
return nil
}
// renderCuddledSequence processes a YAML sequence node which has already been
// determined to be cuddled. We only cuddle sequences of structs or lists
// which have no comments.
func (ky *Encoder) renderCuddledSequence(node *yaml.Node, indent int, flags flagMask, out io.Writer) error {
fmt.Fprint(out, "[")
for i, child := range node.Content {
// Each iteration should leave us cuddled for the next item.
if i > 0 {
fmt.Fprint(out, ", ")
}
if err := ky.renderNode(child, indent, flags, out); err != nil {
return err
}
}
fmt.Fprint(out, "]")
return nil
}
func (ky *Encoder) renderUncuddledSequence(node *yaml.Node, indent int, flags flagMask, out io.Writer) error {
// Get into the right state for the first item.
fmt.Fprint(out, "[\n")
ky.writeIndent(indent, out)
for _, child := range node.Content {
// Each iteration should leave us ready to close the list. Since we
// have an item to render, we need 1 more indent.
ky.writeIndent(1, out)
if len(child.HeadComment) > 0 {
ky.renderComments(child.HeadComment, indent+1, out)
fmt.Fprint(out, "\n")
ky.writeIndent(indent+1, out)
}
if err := ky.renderNode(child, indent+1, flags, out); err != nil {
return err
}
fmt.Fprint(out, ",")
if len(child.LineComment) > 0 {
ky.renderComments(" "+child.LineComment, 0, out)
}
fmt.Fprint(out, "\n")
ky.writeIndent(indent, out)
if len(child.FootComment) > 0 {
ky.writeIndent(1, out)
ky.renderComments(child.FootComment, indent+1, out)
fmt.Fprint(out, "\n")
ky.writeIndent(indent, out)
}
}
fmt.Fprint(out, "]")
return nil
}
func (ky *Encoder) nodeKindString(kind yaml.Kind) string {
switch kind {
case yaml.DocumentNode:
return "document"
case yaml.ScalarNode:
return "scalar"
case yaml.MappingNode:
return "mapping"
case yaml.SequenceNode:
return "sequence"
case yaml.AliasNode:
return "alias"
default:
return "unknown"
}
}
func isCuddledKind(node *yaml.Node) bool {
if node == nil {
return false
}
switch node.Kind {
case yaml.SequenceNode, yaml.MappingNode:
return true
case yaml.AliasNode:
return isCuddledKind(node.Alias)
}
return false
}
// renderMapping processes a YAML mapping node, rendering it to the output. This
// DOES NOT render a trailing newline or head/line/foot comments of the mapping
// itself, but DOES render comments of the child nodes.
func (ky *Encoder) renderMapping(node *yaml.Node, indent int, flags flagMask, out io.Writer) error {
if len(node.Content) == 0 {
fmt.Fprint(out, "{}")
return nil
}
if flags&flagCompact != 0 {
return ky.renderCompactMapping(node, flags, out)
}
joinComments := func(a, b string) string {
if len(a) > 0 && len(b) > 0 {
return a + "\n" + b
}
return a + b
}
fmt.Fprint(out, "{\n")
for i := 0; i < len(node.Content); i += 2 {
key := node.Content[i]
val := node.Content[i+1]
ky.writeIndent(indent+1, out)
// Only one of these should be set.
if comments := joinComments(key.HeadComment, val.HeadComment); len(comments) > 0 {
ky.renderComments(comments, indent+1, out)
fmt.Fprint(out, "\n")
ky.writeIndent(indent+1, out)
}
// Mapping keys are always strings in KYAML, even if the YAML node says
// otherwise.
if err := ky.renderString(key.Value, indent+1, flagLazyQuote|flagCompact, out); err != nil {
return err
}
fmt.Fprint(out, ": ")
if err := ky.renderNode(val, indent+1, flags, out); err != nil {
return err
}
fmt.Fprint(out, ",")
if len(key.LineComment) > 0 && len(val.LineComment) > 0 {
return fmt.Errorf("kyaml internal error: line %d: both key and value have line comments", key.Line)
}
if len(key.LineComment) > 0 {
ky.renderComments(" "+key.LineComment, 0, out)
} else if len(val.LineComment) > 0 {
ky.renderComments(" "+val.LineComment, 0, out)
}
fmt.Fprint(out, "\n")
// Only one of these should be set.
if comments := joinComments(key.FootComment, val.FootComment); len(comments) > 0 {
ky.writeIndent(indent+1, out)
ky.renderComments(comments, indent+1, out)
fmt.Fprint(out, "\n")
}
}
ky.writeIndent(indent, out)
fmt.Fprint(out, "}")
return nil
}
// renderCompactMapping renders a YAML mapping node in compact form.
func (ky *Encoder) renderCompactMapping(node *yaml.Node, flags flagMask, out io.Writer) error {
fmt.Fprint(out, "{")
for i := 0; i < len(node.Content); i += 2 {
key := node.Content[i]
val := node.Content[i+1]
if i > 0 {
fmt.Fprint(out, ", ")
}
// Mapping keys are always strings in KYAML, even if the YAML node says
// otherwise.
if err := ky.renderString(key.Value, 0, flags|flagLazyQuote|flagCompact, out); err != nil {
return err
}
fmt.Fprint(out, ": ")
if err := ky.renderNode(val, 0, flags, out); err != nil {
return err
}
}
fmt.Fprint(out, "}")
return nil
}
func (ky *Encoder) writeIndent(level int, out io.Writer) {
const indentString = " "
for range level {
fmt.Fprint(out, indentString)
}
}
// renderCommentBlock writes the comments node to the output. This assumes the
// cursor is at the right place to start writing and DOES NOT render a trailing
// newline.
func (ky *Encoder) renderComments(comments string, indent int, out io.Writer) {
if len(comments) == 0 {
return
}
lines := strings.Split(comments, "\n")
for i, line := range lines {
if i > 0 {
fmt.Fprint(out, "\n")
ky.writeIndent(indent, out)
}
fmt.Fprint(out, line)
}
}
func (ky *Encoder) renderAlias(node *yaml.Node, indent int, flags flagMask, out io.Writer) error {
if node.Alias != nil {
return ky.renderNode(node.Alias, indent+1, flags, out)
}
return nil
}