Add a flag to switch between patch strategy for unstructured

Signed-off-by: wxdao <waxiadao@gmail.com>
pull/9938/head
wxdao 4 years ago
parent c88c081f47
commit e28152f816

@ -163,6 +163,8 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
} }
actionConfig.RegistryClient = registryClient actionConfig.RegistryClient = registryClient
actionConfig.UseThreeWayMergePatchForUnstructured = settings.UseThreeWayMergePatchForUnstructured
// Add subcommands // Add subcommands
cmd.AddCommand( cmd.AddCommand(
// chart commands // chart commands

@ -16,6 +16,7 @@ require (
github.com/docker/distribution v2.7.1+incompatible github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible
github.com/docker/go-units v0.4.0 github.com/docker/go-units v0.4.0
github.com/evanphx/json-patch v4.9.0+incompatible
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/gofrs/flock v0.8.0 github.com/gofrs/flock v0.8.0
github.com/gosuri/uitable v0.0.4 github.com/gosuri/uitable v0.0.4

@ -94,6 +94,10 @@ type Configuration struct {
// Capabilities describes the capabilities of the Kubernetes cluster. // Capabilities describes the capabilities of the Kubernetes cluster.
Capabilities *chartutil.Capabilities Capabilities *chartutil.Capabilities
// UseThreeWayMergePatchForUnstructured controls whether to use three way merge patch
// for unstructured (CR, CRD etc.) objects.
UseThreeWayMergePatchForUnstructured bool
Log func(string, ...interface{}) Log func(string, ...interface{})
} }
@ -365,6 +369,7 @@ func (cfg *Configuration) recordRelease(r *release.Release) {
func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace, helmDriver string, log DebugLog) error { func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace, helmDriver string, log DebugLog) error {
kc := kube.New(getter) kc := kube.New(getter)
kc.Log = log kc.Log = log
kc.UseThreeWayMergePatchForUnstructured = cfg.UseThreeWayMergePatchForUnstructured
lazyClient := &lazyClient{ lazyClient := &lazyClient{
namespace: namespace, namespace: namespace,

@ -68,6 +68,9 @@ type EnvSettings struct {
PluginsDirectory string PluginsDirectory string
// MaxHistory is the max release history maintained. // MaxHistory is the max release history maintained.
MaxHistory int MaxHistory int
// UseThreeWayMergePatchForUnstructured controls whether to use three way merge patch
// for unstructured (CR, CRD etc.) objects.
UseThreeWayMergePatchForUnstructured bool
} }
func New() *EnvSettings { func New() *EnvSettings {
@ -86,6 +89,7 @@ func New() *EnvSettings {
RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")), RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")),
} }
env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG")) env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG"))
env.UseThreeWayMergePatchForUnstructured, _ = strconv.ParseBool(os.Getenv("HELM_USE_THREE_WAY_MERGE_PATCH_FOR_UNSTRUCTURED"))
// bind to kubernetes config flags // bind to kubernetes config flags
env.config = &genericclioptions.ConfigFlags{ env.config = &genericclioptions.ConfigFlags{
@ -115,6 +119,7 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file") fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file")
fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs") fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs")
fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the file containing cached repository indexes") fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the file containing cached repository indexes")
fs.BoolVar(&s.UseThreeWayMergePatchForUnstructured, "use-three-way-merge-patch-for-unstructured", s.UseThreeWayMergePatchForUnstructured, "use three way merge patch for unstructured (CR, CRD etc.) objects")
} }
func envOr(name, def string) string { func envOr(name, def string) string {

@ -27,6 +27,7 @@ import (
"sync" "sync"
"time" "time"
jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors" "github.com/pkg/errors"
batch "k8s.io/api/batch/v1" batch "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
@ -69,6 +70,10 @@ type Client struct {
// Namespace allows to bypass the kubeconfig file for the choice of the namespace // Namespace allows to bypass the kubeconfig file for the choice of the namespace
Namespace string Namespace string
// UseThreeWayMergePatchForUnstructured controls whether to use three way merge patch
// for unstructured (CR, CRD etc.) objects
UseThreeWayMergePatchForUnstructured bool
kubeClient *kubernetes.Clientset kubeClient *kubernetes.Clientset
} }
@ -418,7 +423,7 @@ func deleteResource(info *resource.Info) error {
return err return err
} }
func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.PatchType, error) { func createPatch(target *resource.Info, current runtime.Object, useThreeWayMergePatchForUnstructured bool) ([]byte, types.PatchType, error) {
oldData, err := json.Marshal(current) oldData, err := json.Marshal(current)
if err != nil { if err != nil {
return nil, types.StrategicMergePatchType, errors.Wrap(err, "serializing current configuration") return nil, types.StrategicMergePatchType, errors.Wrap(err, "serializing current configuration")
@ -446,7 +451,7 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P
// Unstructured objects, such as CRDs, may not have an not registered error // Unstructured objects, such as CRDs, may not have an not registered error
// returned from ConvertToVersion. Anything that's unstructured should // returned from ConvertToVersion. Anything that's unstructured should
// use the jsonmergepatch.CreateThreeWayJSONMergePatch. Strategic Merge Patch is not supported // use generic JSON merge patch. Strategic Merge Patch is not supported
// on objects like CRDs. // on objects like CRDs.
_, isUnstructured := versionedObject.(runtime.Unstructured) _, isUnstructured := versionedObject.(runtime.Unstructured)
@ -455,6 +460,7 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P
if isUnstructured || isCRD { if isUnstructured || isCRD {
// fall back to generic JSON merge patch // fall back to generic JSON merge patch
if useThreeWayMergePatchForUnstructured {
// from https://github.com/kubernetes/kubectl/blob/b83b2ec7d15f286720bccf7872b5c72372cb8e80/pkg/cmd/apply/patcher.go#L129 // from https://github.com/kubernetes/kubectl/blob/b83b2ec7d15f286720bccf7872b5c72372cb8e80/pkg/cmd/apply/patcher.go#L129
preconditions := []mergepatch.PreconditionFunc{mergepatch.RequireKeyUnchanged("apiVersion"), preconditions := []mergepatch.PreconditionFunc{mergepatch.RequireKeyUnchanged("apiVersion"),
mergepatch.RequireKeyUnchanged("kind"), mergepatch.RequireMetadataKeyUnchanged("name")} mergepatch.RequireKeyUnchanged("kind"), mergepatch.RequireMetadataKeyUnchanged("name")}
@ -464,6 +470,9 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P
} }
return patch, types.MergePatchType, err return patch, types.MergePatchType, err
} }
patch, err := jsonpatch.CreateMergePatch(oldData, newData)
return patch, types.MergePatchType, err
}
patchMeta, err := strategicpatch.NewPatchMetaFromStruct(versionedObject) patchMeta, err := strategicpatch.NewPatchMetaFromStruct(versionedObject)
if err != nil { if err != nil {
@ -490,7 +499,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object,
} }
c.Log("Replaced %q with kind %s for kind %s", target.Name, currentObj.GetObjectKind().GroupVersionKind().Kind, kind) c.Log("Replaced %q with kind %s for kind %s", target.Name, currentObj.GetObjectKind().GroupVersionKind().Kind, kind)
} else { } else {
patch, patchType, err := createPatch(target, currentObj) patch, patchType, err := createPatch(target, currentObj, c.UseThreeWayMergePatchForUnstructured)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to create patch") return errors.Wrap(err, "failed to create patch")
} }

Loading…
Cancel
Save