diff --git a/kyaml/kio/byteio_reader.go b/kyaml/kio/byteio_reader.go index fd423120d..43250e0b1 100644 --- a/kyaml/kio/byteio_reader.go +++ b/kyaml/kio/byteio_reader.go @@ -38,6 +38,9 @@ type ByteReadWriter struct { // the Resources, otherwise they will be cleared. KeepReaderAnnotations bool + // AddSeqIndentAnnotation if true adds kioutil.SeqIndentAnnotation to each resource + AddSeqIndentAnnotation bool + // Style is a style that is set on the Resource Node Document. Style yaml.Style @@ -48,16 +51,13 @@ type ByteReadWriter struct { NoWrap bool WrappingAPIVersion string WrappingKind string - - // RetainSeqIndent if true retains the sequence indentation of - RetainSeqIndent bool } func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) { b := &ByteReader{ Reader: rw.Reader, OmitReaderAnnotations: rw.OmitReaderAnnotations, - AddSeqIndentAnnotation: rw.RetainSeqIndent, + AddSeqIndentAnnotation: rw.AddSeqIndentAnnotation, } val, err := b.Read() if rw.FunctionConfig == nil { @@ -117,6 +117,9 @@ type ByteReader struct { // annotation on Resources as they are Read. OmitReaderAnnotations bool + // AddSeqIndentAnnotation if true adds kioutil.SeqIndentAnnotation to each resource + AddSeqIndentAnnotation bool + // SetAnnotations is a map of caller specified annotations to set on resources as they are read // These are independent of the annotations controlled by OmitReaderAnnotations SetAnnotations map[string]string @@ -135,9 +138,6 @@ type ByteReader struct { // WrappingKind is set by Read(), and is the kind of the object that // the read objects were originally wrapped in. WrappingKind string - - // AddSeqIndentAnnotation if true adds kioutil.SeqIndentAnnotation to each resource - AddSeqIndentAnnotation bool } var _ Reader = &ByteReader{} @@ -264,46 +264,35 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) { // value is the input yaml string and node is the decoded equivalent of value // the annotation value is decided by deriving the existing sequence indentation of resource func addSeqIndentAnno(value string, node *yaml.RNode) error { + // step 1: derive the sequence indentation of the node anno := node.GetAnnotations() if anno[kioutil.SeqIndentAnnotation] != "" { // the annotation already exists, so don't change it return nil } - currentDefaultIndent := yaml.SequenceIndentationStyle() - defer yaml.SetSequenceIndentationStyle(currentDefaultIndent) + // marshal the node with 2 space sequence indentation and calculate the diff + out, err := yaml.MarshalWithIndent(node.Document(), yaml.DefaultMapIndent, yaml.WideSeqIndent) + if err != nil { + return err + } + twoSpaceIndentDiff := copyutil.PrettyFileDiff(string(out), value) - // encode the node to string with default 2 space sequence indentation and calculate the diff - yaml.SetSequenceIndentationStyle(yaml.WideSequenceStyle) - n, err := yaml.Parse(value) + // marshal the node with 0 space sequence indentation and calculate the diff + out, err = yaml.MarshalWithIndent(node.Document(), yaml.DefaultMapIndent, yaml.CompactSeqIndent) if err != nil { return err } - out, err := n.String() - if err != nil { - return err - } - twoSpaceIndentDiff := copyutil.PrettyFileDiff(out, value) - - // encode the node to string with compact 0 space sequence indentation and calculate the diff - yaml.SetSequenceIndentationStyle(yaml.CompactSequenceStyle) - n, err = yaml.Parse(value) - if err != nil { - return err - } - out, err = n.String() - if err != nil { - return err - } - noIndentDiff := copyutil.PrettyFileDiff(out, value) + noIndentDiff := copyutil.PrettyFileDiff(string(out), value) var style string if len(noIndentDiff) <= len(twoSpaceIndentDiff) { - style = yaml.CompactSequenceStyle + style = string(yaml.CompactSeqIndent) } else { - style = yaml.WideSequenceStyle + style = string(yaml.WideSeqIndent) } + // step 2: add the annotation to the node return node.PipeE( yaml.LookupCreate(yaml.MappingNode, yaml.MetadataField, yaml.AnnotationsField), yaml.SetField(kioutil.SeqIndentAnnotation, yaml.NewScalarRNode(style))) diff --git a/kyaml/kio/byteio_reader_test.go b/kyaml/kio/byteio_reader_test.go index 3d9bbdcc8..7b74e009a 100644 --- a/kyaml/kio/byteio_reader_test.go +++ b/kyaml/kio/byteio_reader_test.go @@ -806,7 +806,7 @@ items: } } -func TestByteReader_RetainSeqIndent(t *testing.T) { +func TestByteReader_AddSeqIndent(t *testing.T) { type testCase struct { name string err string diff --git a/kyaml/kio/byteio_readwriter_test.go b/kyaml/kio/byteio_readwriter_test.go index fe7490fa3..bb1759c3c 100644 --- a/kyaml/kio/byteio_readwriter_test.go +++ b/kyaml/kio/byteio_readwriter_test.go @@ -581,7 +581,7 @@ spec: w := tc.instance w.Writer = &out w.Reader = &in - w.RetainSeqIndent = true + w.AddSeqIndentAnnotation = true nodes, err := w.Read() if !assert.NoError(t, err) { diff --git a/kyaml/kio/byteio_writer.go b/kyaml/kio/byteio_writer.go index 3d8303935..4f87f794d 100644 --- a/kyaml/kio/byteio_writer.go +++ b/kyaml/kio/byteio_writer.go @@ -89,6 +89,9 @@ func (w ByteWriter) Write(inputNodes []*yaml.RNode) error { if jsonEncodeSingleBareNode { encoder := json.NewEncoder(w.Writer) encoder.SetIndent("", " ") + if err := nodes[0].DeleteAnnotation(kioutil.SeqIndentAnnotation); err != nil { + return errors.Wrap(err) + } return errors.Wrap(encoder.Encode(nodes[0])) } @@ -97,8 +100,7 @@ func (w ByteWriter) Write(inputNodes []*yaml.RNode) error { // don't wrap the elements if w.WrappingKind == "" { for i := range nodes { - setSeqIndent(nodes[i], encoder) - if err := encoder.Encode(nodes[i].Document()); err != nil { + if err := unwrapAndEncodeYAML(nodes[i], encoder); err != nil { return err } } @@ -135,23 +137,21 @@ func (w ByteWriter) Write(inputNodes []*yaml.RNode) error { return encoder.Encode(doc) } -// setSeqIndent reads the SeqIndentAnnotation from node and sets the sequence indentation -// in the encoder -// if the resource doesn't have SeqIndentAnnotation it will use CompactSeqIndent -func setSeqIndent(node *yaml.RNode, encoder *yaml.Encoder) { +// unwrapAndEncodeYAML encodes the yaml RNode in unwrapped format, +// as a pre-step, it clears the sets the sequence indentation for encoder, +// based on the kioutil.SeqIndentAnnotation and clears it before encoding. +func unwrapAndEncodeYAML(node *yaml.RNode, encoder *yaml.Encoder) error { anno := node.GetAnnotations() seqIndent := anno[kioutil.SeqIndentAnnotation] - if seqIndent == yaml.WideSequenceStyle { + if seqIndent == string(yaml.WideSeqIndent) { encoder.DefaultSeqIndent() } else { encoder.CompactSeqIndent() } - if err := node.PipeE(yaml.ClearAnnotation(kioutil.SeqIndentAnnotation)); err != nil { - return - } - if err := yaml.ClearEmptyAnnotations(node); err != nil { - return + if err := node.DeleteAnnotation(kioutil.SeqIndentAnnotation); err != nil { + return errors.Wrap(err) } + return encoder.Encode(node.Document()) } func copyRNodes(in []*yaml.RNode) []*yaml.RNode { diff --git a/kyaml/kio/kioutil/kioutil.go b/kyaml/kio/kioutil/kioutil.go index 8d599448c..993cdfd84 100644 --- a/kyaml/kio/kioutil/kioutil.go +++ b/kyaml/kio/kioutil/kioutil.go @@ -23,7 +23,7 @@ const ( // PathAnnotation records the path to the file the Resource was read from PathAnnotation AnnotationKey = "config.kubernetes.io/path" - // SeqIndentAnnotation records the path to the file the Resource was read from + // SeqIndentAnnotation records the sequence nodes indentation of the input resource SeqIndentAnnotation AnnotationKey = "internal.config.kubernetes.io/seqindent" ) diff --git a/kyaml/kio/pkgio_reader.go b/kyaml/kio/pkgio_reader.go index 49b8457df..08d20f39b 100644 --- a/kyaml/kio/pkgio_reader.go +++ b/kyaml/kio/pkgio_reader.go @@ -178,7 +178,8 @@ type LocalPackageReader struct { // the file FileSkipFunc LocalPackageSkipFileFunc - RetainSeqIndent bool + // AddSeqIndentAnnotation if true adds kioutil.SeqIndentAnnotation to each resource + AddSeqIndentAnnotation bool } var _ Reader = LocalPackageReader{} @@ -269,7 +270,7 @@ func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode Reader: f, OmitReaderAnnotations: r.OmitReaderAnnotations, SetAnnotations: r.SetAnnotations, - AddSeqIndentAnnotation: r.RetainSeqIndent, + AddSeqIndentAnnotation: r.AddSeqIndentAnnotation, } return rr.Read() } diff --git a/kyaml/yaml/alias.go b/kyaml/yaml/alias.go index 8ce609fa9..41d536321 100644 --- a/kyaml/yaml/alias.go +++ b/kyaml/yaml/alias.go @@ -10,24 +10,14 @@ import ( "sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml" ) -const CompactSequenceStyle = "compact" -const WideSequenceStyle = "wide" +const ( + WideSeqIndent SeqIndentType = "wide" + CompactSeqIndent SeqIndentType = "compact" + DefaultMapIndent = 2 +) -const DefaultIndent = 2 -const DefaultSequenceStyle = CompactSequenceStyle - -var sequenceIndentationStyle = DefaultSequenceStyle -var indent = DefaultIndent - -// SetSequenceIndentationStyle sets the sequenceIndentationStyle variable -func SetSequenceIndentationStyle(style string) { - sequenceIndentationStyle = style -} - -// SequenceIndentationStyle returns the value of sequenceIndentationStyle -func SequenceIndentationStyle() string { - return sequenceIndentationStyle -} +// SeqIndentType holds the indentation style for sequence nodes +type SeqIndentType string // Expose the yaml.v3 functions so this package can be used as a replacement @@ -53,13 +43,33 @@ var Unmarshal = yaml.Unmarshal var NewDecoder = yaml.NewDecoder var NewEncoder = func(w io.Writer) *yaml.Encoder { e := yaml.NewEncoder(w) - e.SetIndent(indent) - if sequenceIndentationStyle == CompactSequenceStyle { - e.CompactSeqIndent() - } + e.SetIndent(DefaultMapIndent) + e.CompactSeqIndent() return e } +// MarshalWithIndent marshals the input interface with provided indents +func MarshalWithIndent(in interface{}, mapIndent int, seqIndent SeqIndentType) ([]byte, error) { + var buf bytes.Buffer + err := NewEncoderWithIndent(&buf, mapIndent, seqIndent).Encode(in) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// NewEncoderWithIndent returns the encoder with configurable indents +func NewEncoderWithIndent(w io.Writer, mapIndent int, seqIndent SeqIndentType) *yaml.Encoder { + encoder := NewEncoder(w) + encoder.SetIndent(mapIndent) + if seqIndent == WideSeqIndent { + encoder.DefaultSeqIndent() + } else { + encoder.CompactSeqIndent() + } + return encoder +} + var AliasNode yaml.Kind = yaml.AliasNode var DocumentNode yaml.Kind = yaml.DocumentNode var MappingNode yaml.Kind = yaml.MappingNode diff --git a/kyaml/yaml/rnode.go b/kyaml/yaml/rnode.go index 9d9cbcaf9..f3ba5b079 100644 --- a/kyaml/yaml/rnode.go +++ b/kyaml/yaml/rnode.go @@ -505,6 +505,14 @@ func (rn *RNode) SetAnnotations(m map[string]string) error { return rn.setMapInMetadata(m, AnnotationsField) } +// DeleteAnnotation tries to delete the annotation and clears the empty metadata field. +func (rn *RNode) DeleteAnnotation(annotation string) error { + if err := rn.PipeE(ClearAnnotation(annotation)); err != nil { + return err + } + return ClearEmptyAnnotations(rn) +} + // GetLabels gets the metadata labels field. // If the field is missing, returns an empty map. // Use another method to check for missing metadata.