diff --git a/kyaml/kio/filters/fmtr.go b/kyaml/kio/filters/fmtr.go index 5cb2b0c00..7f2acbda4 100644 --- a/kyaml/kio/filters/fmtr.go +++ b/kyaml/kio/filters/fmtr.go @@ -31,6 +31,20 @@ import ( "sigs.k8s.io/kustomize/kyaml/yaml" ) +type FormattingStrategy = string + +const ( + // NoFmtAnnotation determines if the resource should be formatted. + FmtAnnotation string = "config.kubernetes.io/formatting" + + // FmtStrategyStandard means the resource will be formatted according + // to the default rules. + FmtStrategyStandard FormattingStrategy = "standard" + + // FmtStrategyNone means the resource will not be formatted. + FmtStrategyNone FormattingStrategy = "none" +) + // FormatInput returns the formatted input. func FormatInput(input io.Reader) (*bytes.Buffer, error) { buff := &bytes.Buffer{} @@ -64,6 +78,15 @@ var _ kio.Filter = FormatFilter{} func (f FormatFilter) Filter(slice []*yaml.RNode) ([]*yaml.RNode, error) { for i := range slice { + fmtStrategy, err := getFormattingStrategy(slice[i]) + if err != nil { + return nil, err + } + + if fmtStrategy == FmtStrategyNone { + continue + } + kindNode, err := slice[i].Pipe(yaml.Get("kind")) if err != nil { return nil, err @@ -94,6 +117,28 @@ func (f FormatFilter) Filter(slice []*yaml.RNode) ([]*yaml.RNode, error) { return slice, nil } +// getFormattingStrategy looks for the formatting annotation to determine +// which strategy should be used for formatting. The default is standard +// if no annotation is found. +func getFormattingStrategy(node *yaml.RNode) (FormattingStrategy, error) { + value, err := node.Pipe(yaml.GetAnnotation(FmtAnnotation)) + if err != nil || value == nil { + return FmtStrategyStandard, err + } + + fmtStrategy := value.YNode().Value + + switch fmtStrategy { + case FmtStrategyStandard: + return FmtStrategyStandard, nil + case FmtStrategyNone: + return FmtStrategyNone, nil + default: + return "", fmt.Errorf( + "formatting annotation has illegal value %s", fmtStrategy) + } +} + type formatter struct { apiVersion string kind string diff --git a/kyaml/kio/filters/fmtr_test.go b/kyaml/kio/filters/fmtr_test.go index bff6a0dd6..ca1f2a276 100644 --- a/kyaml/kio/filters/fmtr_test.go +++ b/kyaml/kio/filters/fmtr_test.go @@ -904,3 +904,118 @@ func TestFormatFileOrDirectory_trimWhiteSpace(t *testing.T) { assert.Equal(t, string(testyaml.FormattedYaml1), string(b)) } + +func TestFormatFileOrDirectory_FmtAnnotation(t *testing.T) { + testCases := []struct { + name string + input []byte + expectedOutput []byte + expectError bool + }{ + { + name: "no formatting annotation", + input: testyaml.UnformattedYaml1, + expectedOutput: testyaml.FormattedYaml1, + }, + { + name: "formatting strategy none", + input: []byte(` +spec: a +status: + conditions: + - 3 + - 1 + - 2 +apiVersion: example.com/v1beta1 +kind: MyType +metadata: + annotations: + config.kubernetes.io/formatting: none +`), + expectedOutput: []byte(` +spec: a +status: + conditions: + - 3 + - 1 + - 2 +apiVersion: example.com/v1beta1 +kind: MyType +metadata: + annotations: + config.kubernetes.io/formatting: none +`), + }, + { + name: "formatting strategy standard", + input: []byte(` +spec: a +status: + conditions: + - 3 + - 1 + - 2 +apiVersion: example.com/v1beta1 +kind: MyType +metadata: + annotations: + config.kubernetes.io/formatting: standard +`), + expectedOutput: []byte(` +apiVersion: example.com/v1beta1 +kind: MyType +metadata: + annotations: + config.kubernetes.io/formatting: standard +spec: a +status: + conditions: + - 3 + - 1 + - 2 +`), + }, + { + name: "unknown formatting strategy", + input: []byte(` +spec: a +status: + conditions: + - 3 + - 1 + - 2 +apiVersion: example.com/v1beta1 +kind: MyType +metadata: + annotations: + config.kubernetes.io/formatting: unknown +`), + expectError: true, + }, + } + + for i := range testCases { + test := testCases[i] + t.Run(test.name, func(t *testing.T) { + f, err := ioutil.TempFile("", "yamlfmt*.yaml") + assert.NoError(t, err) + defer os.Remove(f.Name()) + + err = ioutil.WriteFile(f.Name(), test.input, 0600) + assert.NoError(t, err) + + err = FormatFileOrDirectory(f.Name()) + if test.expectError { + assert.Error(t, err) + return + } + assert.NoError(t, err) + + b, err := ioutil.ReadFile(f.Name()) + assert.NoError(t, err) + + assert.Equal(t, strings.TrimSpace(string(test.expectedOutput)), + strings.TrimSpace(string(b))) + }) + } +} diff --git a/kyaml/testutil/testutil.go b/kyaml/testutil/testutil.go new file mode 100644 index 000000000..cce2f2e8f --- /dev/null +++ b/kyaml/testutil/testutil.go @@ -0,0 +1,33 @@ +package testutil_test + +import ( + "bytes" + + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func UpdateYamlString(doc string, functions ...yaml.Filter) (string, error) { + b, err := UpdateYamlBytes([]byte(doc), functions...) + return string(b), err +} + +func UpdateYamlBytes(b []byte, function ...yaml.Filter) ([]byte, error) { + var out bytes.Buffer + rw := kio.ByteReadWriter{ + Reader: bytes.NewBuffer(b), + Writer: &out, + } + err := kio.Pipeline{ + Inputs: []kio.Reader{&rw}, + Filters: []kio.Filter{ + kio.FilterAll(yaml.FilterFunc( + func(node *yaml.RNode) (*yaml.RNode, error) { + return node.Pipe(function...) + }), + ), + }, + Outputs: []kio.Writer{&rw}, + }.Execute() + return out.Bytes(), err +}