Skip to content

Commit 4ef9d63

Browse files
Merge pull request #132942 from thockin/kyaml
Add KYAML support to kubectl Kubernetes-commit: 1451dd1b0873e801e082f3a06a52685bcd68dcac
2 parents a50ecb1 + 94e7333 commit 4ef9d63

File tree

6 files changed

+130
-15
lines changed

6 files changed

+130
-15
lines changed

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ require (
1919
golang.org/x/sync v0.12.0
2020
golang.org/x/text v0.23.0
2121
gopkg.in/evanphx/json-patch.v4 v4.12.0
22-
k8s.io/api v0.0.0-20250724224534-f2279712f874
23-
k8s.io/apimachinery v0.0.0-20250724224258-50e39b11cd32
24-
k8s.io/client-go v0.0.0-20250724224906-d4f2d5b8ccf7
22+
k8s.io/api v0.0.0-20250725024535-b95b43d5b95d
23+
k8s.io/apimachinery v0.0.0-20250725024258-04507a37f6a4
24+
k8s.io/client-go v0.0.0-20250725024918-f78361a6474d
2525
k8s.io/klog/v2 v2.130.1
2626
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
2727
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
2828
sigs.k8s.io/kustomize/api v0.20.1
2929
sigs.k8s.io/kustomize/kyaml v0.20.1
30-
sigs.k8s.io/yaml v1.5.0
30+
sigs.k8s.io/yaml v1.6.0
3131
)
3232

3333
require (

go.sum

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,12 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
170170
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
171171
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
172172
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
173-
k8s.io/api v0.0.0-20250724224534-f2279712f874 h1:AzVs/5TvK2r0BSsV33dVm4FLPgJEUybkjfLrEmiA7Ss=
174-
k8s.io/api v0.0.0-20250724224534-f2279712f874/go.mod h1:h7pQu2oCQ3ccFA95Gaxuu7JO+0gHm6uxdKA4dLNL3Y8=
175-
k8s.io/apimachinery v0.0.0-20250724224258-50e39b11cd32 h1:XbDM37tJSNp0ga7QZkizY+qxKJEA0DFe+nqssKruths=
176-
k8s.io/apimachinery v0.0.0-20250724224258-50e39b11cd32/go.mod h1:mjSAX6740hY31nMwwbFVIcjVaXzV46iZzqDA+UfugZ8=
177-
k8s.io/client-go v0.0.0-20250724224906-d4f2d5b8ccf7 h1:C8WJw/YbIDqiA23gjDMRgBKsALuaO0i3iF2w574EpfU=
178-
k8s.io/client-go v0.0.0-20250724224906-d4f2d5b8ccf7/go.mod h1:zKs1Ap5k23bvGabDhvkL2sweYIp3KNomdmyrhOBwvzo=
173+
k8s.io/api v0.0.0-20250725024535-b95b43d5b95d h1:OdC1L69BYvh/4HX6Wgg7DeL0A++gD3gadKfaWT4IdbA=
174+
k8s.io/api v0.0.0-20250725024535-b95b43d5b95d/go.mod h1:wKZv1VB6nzJ6L449TteVelrBfRsawhrthiOsEylKo8U=
175+
k8s.io/apimachinery v0.0.0-20250725024258-04507a37f6a4 h1:N25HX4lRPTvLHSUPoCMFP+B/oEcOmPESB+BRkYMD8Io=
176+
k8s.io/apimachinery v0.0.0-20250725024258-04507a37f6a4/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
177+
k8s.io/client-go v0.0.0-20250725024918-f78361a6474d h1:8i9q3fd352G8FsurDLj4++5oMS4gWqZ5O2lErveG9Ok=
178+
k8s.io/client-go v0.0.0-20250725024918-f78361a6474d/go.mod h1:j4aKw1XACZSFUnRBqWNU0d3TXbXzaBhAbazCrGTYHdg=
179179
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
180180
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
181181
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
@@ -192,5 +192,5 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
192192
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
193193
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
194194
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
195-
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
196-
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
195+
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
196+
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

pkg/genericclioptions/json_yaml_flags.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package genericclioptions
1818

1919
import (
20+
"os"
21+
"slices"
2022
"strings"
2123

2224
"github.com/spf13/cobra"
@@ -29,7 +31,12 @@ func (f *JSONYamlPrintFlags) AllowedFormats() []string {
2931
if f == nil {
3032
return []string{}
3133
}
32-
return []string{"json", "yaml"}
34+
formats := []string{"json", "yaml"}
35+
// We can't use the cmdutil pkg directly because of import cycle.
36+
if strings.ToLower(os.Getenv("KUBECTL_KYAML")) == "true" {
37+
formats = append(formats, "kyaml")
38+
}
39+
return formats
3340
}
3441

3542
// JSONYamlPrintFlags provides default flags necessary for json/yaml printing.
@@ -47,11 +54,19 @@ func (f *JSONYamlPrintFlags) ToPrinter(outputFormat string) (printers.ResourcePr
4754
var printer printers.ResourcePrinter
4855

4956
outputFormat = strings.ToLower(outputFormat)
57+
58+
valid := f.AllowedFormats()
59+
if !slices.Contains(valid, outputFormat) {
60+
return nil, NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: valid}
61+
}
62+
5063
switch outputFormat {
5164
case "json":
5265
printer = &printers.JSONPrinter{}
5366
case "yaml":
5467
printer = &printers.YAMLPrinter{}
68+
case "kyaml":
69+
printer = &printers.KYAMLPrinter{}
5570
default:
5671
return nil, NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: f.AllowedFormats()}
5772
}

pkg/printers/json_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,9 @@ func TestPrintersSuccess(t *testing.T) {
105105
om := func(name string) metav1.ObjectMeta { return metav1.ObjectMeta{Name: name} }
106106

107107
genericPrinters := map[string]ResourcePrinter{
108-
"json": NewTypeSetter(scheme.Scheme).ToPrinter(&JSONPrinter{}),
109-
"yaml": NewTypeSetter(scheme.Scheme).ToPrinter(&YAMLPrinter{}),
108+
"json": NewTypeSetter(scheme.Scheme).ToPrinter(&JSONPrinter{}),
109+
"yaml": NewTypeSetter(scheme.Scheme).ToPrinter(&YAMLPrinter{}),
110+
"kyaml": NewTypeSetter(scheme.Scheme).ToPrinter(&KYAMLPrinter{}),
110111
}
111112
objects := map[string]runtime.Object{
112113
"pod": &v1.Pod{ObjectMeta: om("pod")},

pkg/printers/kyaml.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// package kyaml provides a printer for Kubernetes objects that formats them
18+
// as KYAML, a strict subset of YAML that is designed to be explicit and
19+
// unambiguous. KYAML is YAML.
20+
package printers
21+
22+
import (
23+
"bytes"
24+
"errors"
25+
"fmt"
26+
"io"
27+
"reflect"
28+
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/apimachinery/pkg/runtime"
31+
"sigs.k8s.io/yaml/kyaml"
32+
)
33+
34+
// KYAMLPrinter is an implementation of ResourcePrinter which formats data into
35+
// a specific dialect of YAML, known as KYAML. KYAML is halfway between YAML
36+
// and JSON, but is a strict subset of YAML, and so it should should be
37+
// readable by any YAML parser. It is designed to be explicit and unambiguous,
38+
// and eschews significant whitespace.
39+
type KYAMLPrinter struct {
40+
encoder kyaml.Encoder
41+
}
42+
43+
// PrintObj prints the data as KYAML to the specified writer.
44+
func (p *KYAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
45+
// We use reflect.Indirect here in order to obtain the actual value from a pointer.
46+
// We need an actual value in order to retrieve the package path for an object.
47+
// Using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
48+
if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
49+
return errors.New(InternalObjectPrinterErr)
50+
}
51+
52+
switch obj := obj.(type) {
53+
case *metav1.WatchEvent:
54+
if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj.Object.Object)).Type().PkgPath()) {
55+
return errors.New(InternalObjectPrinterErr)
56+
}
57+
case *runtime.Unknown:
58+
return p.encoder.FromYAML(bytes.NewReader(obj.Raw), w)
59+
}
60+
61+
if obj.GetObjectKind().GroupVersionKind().Empty() {
62+
return fmt.Errorf("missing apiVersion or kind; try GetObjectKind().SetGroupVersionKind() if you know the type")
63+
}
64+
65+
return p.encoder.FromObject(obj, w)
66+
}

pkg/printers/kyaml_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package printers
18+
19+
import (
20+
"testing"
21+
22+
"sigs.k8s.io/yaml"
23+
24+
"k8s.io/client-go/kubernetes/scheme"
25+
)
26+
27+
func TestKYAMLPrinter(t *testing.T) {
28+
testPrinter(t, NewTypeSetter(scheme.Scheme).ToPrinter(&KYAMLPrinter{}), kyamlUnmarshal)
29+
}
30+
31+
func kyamlUnmarshal(data []byte, v interface{}) error {
32+
return yaml.Unmarshal(data, v)
33+
}

0 commit comments

Comments
 (0)