mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Merge pull request #3821 from phanimarupaka/OverrideIsSameResourceImpl
Support multiple implementations for IsSameResource
This commit is contained in:
@@ -46,12 +46,13 @@ type Merge3Runner struct {
|
||||
path bool
|
||||
}
|
||||
|
||||
func (r *Merge3Runner) runE(c *cobra.Command, args []string) error {
|
||||
func (r *Merge3Runner) runE(_ *cobra.Command, _ []string) error {
|
||||
matcher := filters.DefaultGVKNNMatcher{MergeOnPath: r.path}
|
||||
err := filters.Merge3{
|
||||
OriginalPath: r.ancestor,
|
||||
UpdatedPath: r.fromDir,
|
||||
DestPath: r.toDir,
|
||||
MergeOnPath: r.path,
|
||||
Matcher: &matcher,
|
||||
}.Merge()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -19,17 +19,24 @@ const (
|
||||
mergeSourceDest = "dest"
|
||||
)
|
||||
|
||||
// ResourceMatcher interface is used to match two resources based on IsSameResource implementation
|
||||
// This is the way to group same logical resources in upstream, local and origin for merge
|
||||
// The default way to group them is using GVKNN similar to how kubernetes server identifies resources
|
||||
// Users of this library might have their own interpretation of grouping similar resources
|
||||
// for e.g. if consumer adds a name-prefix to local resource, it should not be treated as new resource
|
||||
// for updates etc.
|
||||
// Hence, the callers of this library may pass different implementation for IsSameResource
|
||||
type ResourceMatcher interface {
|
||||
IsSameResource(node1, node2 *yaml.RNode) bool
|
||||
}
|
||||
|
||||
// Merge3 performs a 3-way merge on the original, updated, and destination packages.
|
||||
type Merge3 struct {
|
||||
OriginalPath string
|
||||
UpdatedPath string
|
||||
DestPath string
|
||||
MatchFilesGlob []string
|
||||
|
||||
// MergeOnPath will use the relative filepath as part of the merge key.
|
||||
// This may be necessary if the directory contains multiple copies of
|
||||
// the same resource, or resources patches.
|
||||
MergeOnPath bool
|
||||
Matcher ResourceMatcher
|
||||
}
|
||||
|
||||
func (m Merge3) Merge() error {
|
||||
@@ -67,7 +74,11 @@ func (m Merge3) Merge() error {
|
||||
// Filter combines Resources with the same GVK + N + NS into tuples, and then merges them
|
||||
func (m Merge3) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
// index the nodes by their identity
|
||||
tl := tuples{mergeOnPath: m.MergeOnPath}
|
||||
matcher := m.Matcher
|
||||
if matcher == nil {
|
||||
matcher = &DefaultGVKNNMatcher{MergeOnPath: true}
|
||||
}
|
||||
tl := tuples{matcher: matcher}
|
||||
for i := range nodes {
|
||||
if err := tl.add(nodes[i]); err != nil {
|
||||
return nil, err
|
||||
@@ -82,7 +93,7 @@ func (m Merge3) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
case t.original == nil && t.updated == nil && t.dest != nil:
|
||||
// added locally -- keep dest
|
||||
output = append(output, t.dest)
|
||||
case t.original == nil && t.updated != nil && t.dest == nil:
|
||||
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:
|
||||
@@ -109,13 +120,35 @@ func (m Merge3) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
type tuples struct {
|
||||
list []*tuple
|
||||
|
||||
// mergeOnPath if set to true will use the resource filepath
|
||||
// as part of the merge key
|
||||
mergeOnPath bool
|
||||
// matcher matches the resources for merge
|
||||
matcher ResourceMatcher
|
||||
}
|
||||
|
||||
// isSameResource returns true if meta1 and meta2 are for the same logic resource
|
||||
func (ts *tuples) isSameResource(meta1, meta2 yaml.ResourceMeta) bool {
|
||||
// DefaultGVKNNMatcher holds the default matching of resources implementation based on
|
||||
// Group, Version, Kind, Name and Namespace of the resource
|
||||
type DefaultGVKNNMatcher struct {
|
||||
// MergeOnPath will use the relative filepath as part of the merge key.
|
||||
// This may be necessary if the directory contains multiple copies of
|
||||
// the same resource, or resources patches.
|
||||
MergeOnPath bool
|
||||
}
|
||||
|
||||
// IsSameResource returns true if metadata of node1 and metadata of node2 belongs to same logical resource
|
||||
func (dm *DefaultGVKNNMatcher) IsSameResource(node1, node2 *yaml.RNode) bool {
|
||||
if node1 == nil || node2 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
meta1, err := node1.GetMeta()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
meta2, err := node2.GetMeta()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if meta1.Name != meta2.Name {
|
||||
return false
|
||||
}
|
||||
@@ -128,7 +161,7 @@ func (ts *tuples) isSameResource(meta1, meta2 yaml.ResourceMeta) bool {
|
||||
if meta1.Kind != meta2.Kind {
|
||||
return false
|
||||
}
|
||||
if ts.mergeOnPath {
|
||||
if dm.MergeOnPath {
|
||||
// directories may contain multiple copies of a resource with the same
|
||||
// name, namespace, apiVersion and kind -- e.g. kustomize patches, or
|
||||
// multiple environments
|
||||
@@ -143,17 +176,13 @@ func (ts *tuples) isSameResource(meta1, meta2 yaml.ResourceMeta) bool {
|
||||
|
||||
// add adds a node to the list, combining it with an existing matching Resource if found
|
||||
func (ts *tuples) add(node *yaml.RNode) error {
|
||||
nodeMeta, err := node.GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range ts.list {
|
||||
t := ts.list[i]
|
||||
if ts.isSameResource(t.meta, nodeMeta) {
|
||||
if ts.matcher.IsSameResource(addedNode(t), node) {
|
||||
return t.add(node)
|
||||
}
|
||||
}
|
||||
t := &tuple{meta: nodeMeta}
|
||||
t := &tuple{}
|
||||
if err := t.add(node); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -161,9 +190,19 @@ func (ts *tuples) add(node *yaml.RNode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// addedNode returns one on the existing added nodes in the tuple
|
||||
func addedNode(t *tuple) *yaml.RNode {
|
||||
if t.updated != nil {
|
||||
return t.updated
|
||||
}
|
||||
if t.original != nil {
|
||||
return t.original
|
||||
}
|
||||
return t.dest
|
||||
}
|
||||
|
||||
// tuple wraps an original, updated, and dest tuple for a given Resource
|
||||
type tuple struct {
|
||||
meta yaml.ResourceMeta
|
||||
original *yaml.RNode
|
||||
updated *yaml.RNode
|
||||
dest *yaml.RNode
|
||||
@@ -178,17 +217,17 @@ func (t *tuple) add(node *yaml.RNode) error {
|
||||
switch meta.Annotations[mergeSourceAnnotation] {
|
||||
case mergeSourceDest:
|
||||
if t.dest != nil {
|
||||
return fmt.Errorf("dest source already specified")
|
||||
return duplicateError("local", meta.Annotations[kioutil.PathAnnotation])
|
||||
}
|
||||
t.dest = node
|
||||
case mergeSourceOriginal:
|
||||
if t.original != nil {
|
||||
return fmt.Errorf("original source already specified")
|
||||
return duplicateError("original upstream", meta.Annotations[kioutil.PathAnnotation])
|
||||
}
|
||||
t.original = node
|
||||
case mergeSourceUpdated:
|
||||
if t.updated != nil {
|
||||
return fmt.Errorf("updated source already specified")
|
||||
return duplicateError("updated upstream", meta.Annotations[kioutil.PathAnnotation])
|
||||
}
|
||||
t.updated = node
|
||||
default:
|
||||
@@ -201,3 +240,8 @@ func (t *tuple) add(node *yaml.RNode) error {
|
||||
func (t *tuple) merge() (*yaml.RNode, error) {
|
||||
return merge3.Merge(t.dest, t.original, t.updated)
|
||||
}
|
||||
|
||||
// duplicateError returns duplicate resources 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)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ func TestMerge3_Merge(t *testing.T) {
|
||||
OriginalPath: filepath.Join(datadir, "dataset1"),
|
||||
UpdatedPath: filepath.Join(datadir, "dataset1-remoteupdates"),
|
||||
DestPath: filepath.Join(dir, "dataset1"),
|
||||
Matcher: &filters.DefaultGVKNNMatcher{MergeOnPath: false},
|
||||
}.Merge()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
@@ -89,7 +90,6 @@ func TestMerge3_Merge_path(t *testing.T) {
|
||||
OriginalPath: filepath.Join(datadir, "dataset1"),
|
||||
UpdatedPath: filepath.Join(datadir, "dataset1-remoteupdates"),
|
||||
DestPath: filepath.Join(dir, "dataset1"),
|
||||
MergeOnPath: true,
|
||||
}.Merge()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
@@ -135,6 +135,7 @@ func TestMerge3_Merge_fail(t *testing.T) {
|
||||
OriginalPath: filepath.Join(datadir, "dataset1"),
|
||||
UpdatedPath: filepath.Join(datadir, "dataset1-remoteupdates"),
|
||||
DestPath: filepath.Join(dir, "dataset1"),
|
||||
Matcher: &filters.DefaultGVKNNMatcher{MergeOnPath: false},
|
||||
}.Merge()
|
||||
if !assert.Error(t, err) {
|
||||
t.FailNow()
|
||||
|
||||
Reference in New Issue
Block a user