From 95e242353b05683818b9db3a8556c309afd8ac9b Mon Sep 17 00:00:00 2001 From: Morten Torkildsen Date: Thu, 10 Jun 2021 17:42:47 -0700 Subject: [PATCH] Allow users to customize handling of deleted resources for merge3 --- kyaml/kio/filters/merge3.go | 91 +++++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/kyaml/kio/filters/merge3.go b/kyaml/kio/filters/merge3.go index 30932800c..d767ea46b 100644 --- a/kyaml/kio/filters/merge3.go +++ b/kyaml/kio/filters/merge3.go @@ -30,6 +30,36 @@ type ResourceMatcher interface { IsSameResource(node1, node2 *yaml.RNode) bool } +// ResourceMergeStrategy is the return type from the Handle function in the +// ResourceHandler interface. It determines which version of a resource should +// be included in the output (if any). +type ResourceMergeStrategy int + +const ( + // Merge means the output to dest should be the 3-way merge of original, + // updated and dest. + Merge ResourceMergeStrategy = iota + // KeepDest means the version of the resource in dest should be the output. + KeepDest + // KeepUpdated means the version of the resource in updated should be the + // output. + KeepUpdated + // KeepOriginal means the version of the resource in original should be the + // output. + KeepOriginal + // Skip means the resource should not be included in the output. + Skip +) + +// ResourceHandler interface is used to determine what should be done for a +// resource once the versions in original, updated and dest has been +// identified based on the ResourceMatcher. This allows users to customize +// what should be the result in dest if a resource has been deleted from +// upstream. +type ResourceHandler interface { + Handle(original, updated, dest *yaml.RNode) ResourceMergeStrategy +} + // Merge3 performs a 3-way merge on the original, updated, and destination packages. type Merge3 struct { OriginalPath string @@ -37,6 +67,7 @@ type Merge3 struct { DestPath string MatchFilesGlob []string Matcher ResourceMatcher + Handler ResourceHandler } func (m Merge3) Merge() error { @@ -78,6 +109,11 @@ func (m Merge3) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { if matcher == nil { matcher = &DefaultGVKNNMatcher{MergeOnPath: true} } + handler := m.Handler + if handler == nil { + handler = &DefaultResourceHandler{} + } + tl := tuples{matcher: matcher} for i := range nodes { if err := tl.add(nodes[i]); err != nil { @@ -89,21 +125,9 @@ func (m Merge3) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { var output []*yaml.RNode for i := range tl.list { t := tl.list[i] - switch { - case t.original == nil && t.updated == nil && t.dest != nil: - // added locally -- keep dest - output = append(output, t.dest) - case t.updated != nil && t.dest == nil: - // added in the update -- add update - output = append(output, t.updated) - case t.original != nil && t.updated == nil: - // deleted in the update - // don't include the resource in the output - case t.original != nil && t.dest == nil: - // deleted locally - // don't include the resource in the output - default: - // dest and updated are non-nil -- merge them + strategy := handler.Handle(t.original, t.updated, t.dest) + switch strategy { + case Merge: node, err := t.merge() if err != nil { return nil, err @@ -111,6 +135,14 @@ func (m Merge3) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { if node != nil { output = append(output, node) } + case KeepDest: + output = append(output, t.dest) + case KeepUpdated: + output = append(output, t.updated) + case KeepOriginal: + output = append(output, t.original) + case Skip: + // do nothing } } return output, nil @@ -245,3 +277,32 @@ func (t *tuple) merge() (*yaml.RNode, error) { func duplicateError(source, filePath string) error { return fmt.Errorf(`found duplicate %q resources in file %q, please refer to "update" documentation for the fix`, source, filePath) } + +// DefaultResourceHandler is the default implementation of the ResourceHandler +// interface. It uses the following rules: +// * Keep dest if resource only exists in dest. +// * Keep updated if resource added in updated. +// * Delete dest if updated has been deleted. +// * Don't add the resource back if removed from dest. +// * Otherwise merge. +type DefaultResourceHandler struct{} + +func (*DefaultResourceHandler) Handle(original, updated, dest *yaml.RNode) ResourceMergeStrategy { + switch { + case original == nil && updated == nil && dest != nil: + // added locally -- keep dest + return KeepDest + case updated != nil && dest == nil: + // added in the update -- add update + return KeepUpdated + case original != nil && updated == nil: + // deleted in the update + return Skip + case original != nil && dest == nil: + // deleted locally + return Skip + default: + // dest and updated are non-nil -- merge them + return Merge + } +}