Skip to content

Commit

Permalink
Extract conflict detection to it's own interface.
Browse files Browse the repository at this point in the history
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
monopole committed Dec 6, 2020
1 parent c63dfd6 commit 62d14b5
Show file tree
Hide file tree
Showing 22 changed files with 418 additions and 444 deletions.
5 changes: 5 additions & 0 deletions api/builtins/PatchStrategicMergeTransformer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -226,6 +227,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
Expand Down Expand Up @@ -367,6 +369,7 @@ github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
Expand Down
23 changes: 23 additions & 0 deletions api/internal/conflict/factory.go
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
}
28 changes: 28 additions & 0 deletions api/internal/conflict/smpatchmergeonlydetector.go
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
}
43 changes: 43 additions & 0 deletions api/internal/k8sdeps/conflict/conflictdetectorjson.go
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
}
65 changes: 65 additions & 0 deletions api/internal/k8sdeps/conflict/conflictdetectorsm.go
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
}
45 changes: 45 additions & 0 deletions api/internal/k8sdeps/conflict/factory.go
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
}
Loading

0 comments on commit 62d14b5

Please sign in to comment.