forked from kubernetes-sigs/kustomize
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract conflict detection to it's own interface.
This PR - defines a patch conflict detector interface, - extracts implementations of the interface from the merginator code, making the merginator code independent of --enable_kyaml. - injects those implementations into kustomize as a function of --enable_kyaml. So, instead of using different merginators to combine resmaps, this pr allows the use of a single patch merge code path that uses different conflict detectors. So instead of debating how to merge, we're now only considering whether to warn on conflict detection in one transformer. This PR is in service of kubernetes-sigs#3304, eliminating seven instances where --enable_kyaml was consulted. These were cases where conflict detection wasn't an issue (but merging patches was).
- Loading branch information
Showing
22 changed files
with
418 additions
and
444 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2020 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package conflict | ||
|
||
import ( | ||
"sigs.k8s.io/kustomize/api/resid" | ||
"sigs.k8s.io/kustomize/api/resource" | ||
) | ||
|
||
type cdFactory struct{} | ||
|
||
var _ resource.ConflictDetectorFactory = &cdFactory{} | ||
|
||
// NewFactory returns a new conflict detector factory. | ||
func NewFactory() resource.ConflictDetectorFactory { | ||
return &cdFactory{} | ||
} | ||
|
||
// New returns an instance of smPatchMergeOnlyDetector. | ||
func (c cdFactory) New(_ resid.Gvk) (resource.ConflictDetector, error) { | ||
return &smPatchMergeOnlyDetector{}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright 2020 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package conflict | ||
|
||
import ( | ||
"sigs.k8s.io/kustomize/api/resource" | ||
) | ||
|
||
// smPatchMergeOnlyDetector ignores conflicts, | ||
// but does real strategic merge patching. | ||
// This is part of an effort to eliminate dependence on | ||
// apimachinery package to allow kustomize integration | ||
// into kubectl (#2506 and #1500) | ||
type smPatchMergeOnlyDetector struct{} | ||
|
||
var _ resource.ConflictDetector = &smPatchMergeOnlyDetector{} | ||
|
||
func (c *smPatchMergeOnlyDetector) HasConflict( | ||
_, _ *resource.Resource) (bool, error) { | ||
return false, nil | ||
} | ||
|
||
func (c *smPatchMergeOnlyDetector) MergePatches( | ||
r, patch *resource.Resource) (*resource.Resource, error) { | ||
err := r.ApplySmPatch(patch) | ||
return r, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Copyright 2019 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package conflict | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
jsonpatch "github.com/evanphx/json-patch" | ||
"k8s.io/apimachinery/pkg/util/mergepatch" | ||
"sigs.k8s.io/kustomize/api/resource" | ||
) | ||
|
||
// conflictDetectorJson detects conflicts in a list of JSON patches. | ||
type conflictDetectorJson struct { | ||
resourceFactory *resource.Factory | ||
} | ||
|
||
var _ resource.ConflictDetector = &conflictDetectorJson{} | ||
|
||
func (cd *conflictDetectorJson) HasConflict( | ||
p1, p2 *resource.Resource) (bool, error) { | ||
return mergepatch.HasConflicts(p1.Map(), p2.Map()) | ||
} | ||
|
||
func (cd *conflictDetectorJson) MergePatches( | ||
patch1, patch2 *resource.Resource) (*resource.Resource, error) { | ||
baseBytes, err := json.Marshal(patch1.Map()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
patchBytes, err := json.Marshal(patch2.Map()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
mergedBytes, err := jsonpatch.MergeMergePatches(baseBytes, patchBytes) | ||
if err != nil { | ||
return nil, err | ||
} | ||
mergedMap := make(map[string]interface{}) | ||
err = json.Unmarshal(mergedBytes, &mergedMap) | ||
return cd.resourceFactory.FromMap(mergedMap), err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// Copyright 2019 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package conflict | ||
|
||
import ( | ||
"fmt" | ||
|
||
"k8s.io/apimachinery/pkg/util/strategicpatch" | ||
"sigs.k8s.io/kustomize/api/resource" | ||
) | ||
|
||
// conflictDetectorSm detects conflicts in a list of strategic merge patches. | ||
type conflictDetectorSm struct { | ||
lookupPatchMeta strategicpatch.LookupPatchMeta | ||
resourceFactory *resource.Factory | ||
} | ||
|
||
var _ resource.ConflictDetector = &conflictDetectorSm{} | ||
|
||
func (cd *conflictDetectorSm) HasConflict( | ||
p1, p2 *resource.Resource) (bool, error) { | ||
return strategicpatch.MergingMapsHaveConflicts( | ||
p1.Map(), p2.Map(), cd.lookupPatchMeta) | ||
} | ||
|
||
func (cd *conflictDetectorSm) MergePatches( | ||
patch1, patch2 *resource.Resource) (*resource.Resource, error) { | ||
if cd.hasDeleteDirectiveMarker(patch2.Map()) { | ||
if cd.hasDeleteDirectiveMarker(patch1.Map()) { | ||
return nil, fmt.Errorf( | ||
"cannot merge patches both containing '$patch: delete' directives") | ||
} | ||
patch1, patch2 = patch2, patch1 | ||
} | ||
mergedMap, err := strategicpatch.MergeStrategicMergeMapPatchUsingLookupPatchMeta( | ||
cd.lookupPatchMeta, patch1.Map(), patch2.Map()) | ||
return cd.resourceFactory.FromMap(mergedMap), err | ||
} | ||
|
||
func (cd *conflictDetectorSm) hasDeleteDirectiveMarker( | ||
patch map[string]interface{}) bool { | ||
if v, ok := patch["$patch"]; ok && v == "delete" { | ||
return true | ||
} | ||
for _, v := range patch { | ||
switch typedV := v.(type) { | ||
case map[string]interface{}: | ||
if cd.hasDeleteDirectiveMarker(typedV) { | ||
return true | ||
} | ||
case []interface{}: | ||
for _, sv := range typedV { | ||
typedE, ok := sv.(map[string]interface{}) | ||
if !ok { | ||
break | ||
} | ||
if cd.hasDeleteDirectiveMarker(typedE) { | ||
return true | ||
} | ||
} | ||
} | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright 2020 The Kubernetes Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package conflict | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
sp "k8s.io/apimachinery/pkg/util/strategicpatch" | ||
"k8s.io/client-go/kubernetes/scheme" | ||
"sigs.k8s.io/kustomize/api/resid" | ||
"sigs.k8s.io/kustomize/api/resource" | ||
) | ||
|
||
type cdFactory struct { | ||
rf *resource.Factory | ||
} | ||
|
||
var _ resource.ConflictDetectorFactory = &cdFactory{} | ||
|
||
// NewFactory returns a conflict detector factory. | ||
// The detector uses a resource factory to convert resources to/from | ||
// json/yaml/maps representations. | ||
func NewFactory(rf *resource.Factory) resource.ConflictDetectorFactory { | ||
return &cdFactory{rf: rf} | ||
} | ||
|
||
// New returns a conflict detector that's aware of the GVK type. | ||
func (f *cdFactory) New(gvk resid.Gvk) (resource.ConflictDetector, error) { | ||
// Convert to apimachinery representation of object | ||
obj, err := scheme.Scheme.New(schema.GroupVersionKind{ | ||
Group: gvk.Group, | ||
Version: gvk.Version, | ||
Kind: gvk.Kind, | ||
}) | ||
if err == nil { | ||
meta, err := sp.NewPatchMetaFromStruct(obj) | ||
return &conflictDetectorSm{ | ||
lookupPatchMeta: meta, resourceFactory: f.rf}, err | ||
} | ||
if runtime.IsNotRegisteredError(err) { | ||
return &conflictDetectorJson{resourceFactory: f.rf}, nil | ||
} | ||
return nil, err | ||
} |
Oops, something went wrong.