pull/11650/merge
Jason Coté 7 months ago committed by GitHub
commit 2e197f4d62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -191,6 +191,7 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
f.StringVar(&client.DryRunOption, "dry-run", "", "simulate an install. If --dry-run is set with no option being specified or as '--dry-run=client', it will not attempt cluster connections. Setting '--dry-run=server' allows attempting cluster connections.")
f.Lookup("dry-run").NoOptDefVal = "client"
f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
f.BoolVar(&client.Force3WayMergePatch, "force-3-way-merge-patch", false, "forces use of a 3-way merge patch in cases where a 2-way merge is still the default")
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
f.BoolVar(&client.Replace, "replace", false, "reuse the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")

@ -79,6 +79,7 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.DryRun, "dry-run", false, "simulate a rollback")
f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
f.BoolVar(&client.Force, "force", false, "force resource update through delete/recreate if needed")
f.BoolVar(&client.Force3WayMergePatch, "force-3-way-merge-patch", false, "forces use of a 3-way merge patch in cases where a 2-way merge is still the default")
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during rollback")
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")

@ -131,6 +131,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
instClient.CreateNamespace = createNamespace
instClient.ChartPathOptions = client.ChartPathOptions
instClient.Force = client.Force
instClient.Force3WayMergePatch = client.Force3WayMergePatch
instClient.DryRun = client.DryRun
instClient.DryRunOption = client.DryRunOption
instClient.DisableHooks = client.DisableHooks
@ -271,6 +272,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
f.MarkDeprecated("recreate-pods", "functionality will no longer be updated. Consult the documentation for other methods to recreate pods")
f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
f.BoolVar(&client.Force3WayMergePatch, "force-3-way-merge-patch", false, "forces use of a 3-way merge patch in cases where a 2-way merge is still the default")
f.BoolVar(&client.DisableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the upgrade process will not validate rendered templates against the Kubernetes OpenAPI Schema")
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed when an upgrade is performed with install flag enabled. By default, CRDs are installed if not already present, when an upgrade is performed with install flag enabled")

@ -71,6 +71,7 @@ type Install struct {
ClientOnly bool
Force bool
Force3WayMergePatch bool
CreateNamespace bool
DryRun bool
DryRunOption string
@ -459,7 +460,7 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource
if len(toBeAdopted) == 0 && len(resources) > 0 {
_, err = i.cfg.KubeClient.Create(resources)
} else if len(resources) > 0 {
_, err = i.cfg.KubeClient.Update(toBeAdopted, resources, i.Force)
_, err = i.cfg.KubeClient.Update(toBeAdopted, resources, i.Force, i.Force3WayMergePatch)
}
if err != nil {
return rel, err

@ -43,6 +43,7 @@ type Rollback struct {
DryRun bool
Recreate bool // will (if true) recreate pods after a rollback.
Force bool // will (if true) force resource upgrade through uninstall/recreate if needed
Force3WayMergePatch bool
CleanupOnFail bool
MaxHistory int // MaxHistory limits the maximum number of revisions saved per release
}
@ -188,7 +189,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
if err != nil {
return targetRelease, errors.Wrap(err, "unable to set metadata visitor from target release")
}
results, err := r.cfg.KubeClient.Update(current, target, r.Force)
results, err := r.cfg.KubeClient.Update(current, target, r.Force, r.Force3WayMergePatch)
if err != nil {
msg := fmt.Sprintf("Rollback %q failed: %s", targetRelease.Name, err)

@ -81,6 +81,8 @@ type Upgrade struct {
//
// This should be used with caution.
Force bool
// Forces use of a 3-way merge patch in cases where a 2-way merge is still the default
Force3WayMergePatch bool
// ResetValues will reset the values to the chart's built-ins rather than merging with existing.
ResetValues bool
// ReuseValues will reuse the user's last supplied values.
@ -426,7 +428,7 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele
u.cfg.Log("upgrade hooks disabled for %s", upgradedRelease.Name)
}
results, err := u.cfg.KubeClient.Update(current, target, u.Force)
results, err := u.cfg.KubeClient.Update(current, target, u.Force, u.Force3WayMergePatch)
if err != nil {
u.cfg.recordRelease(originalRelease)
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)

@ -46,6 +46,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/jsonmergepatch"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/cli-runtime/pkg/genericclioptions"
@ -386,7 +387,7 @@ func (c *Client) BuildTable(reader io.Reader, validate bool) (ResourceList, erro
// occurs, a Result will still be returned with the error, containing all
// resource updates, creations, and deletions that were attempted. These can be
// used for cleanup or other logging purposes.
func (c *Client) Update(original, target ResourceList, force bool) (*Result, error) {
func (c *Client) Update(original, target ResourceList, force, force3WayMergePatch bool) (*Result, error) {
updateErrors := []string{}
res := &Result{}
@ -421,7 +422,7 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err
return errors.Errorf("no %s with the name %q found", kind, info.Name)
}
if err := updateResource(c, info, originalInfo.Object, force); err != nil {
if err := updateResource(c, info, originalInfo.Object, force, force3WayMergePatch); err != nil {
c.Log("error updating the resource %q:\n\t %v", info.Name, err)
updateErrors = append(updateErrors, err.Error())
}
@ -617,7 +618,7 @@ func deleteResource(info *resource.Info, policy metav1.DeletionPropagation) erro
})
}
func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.PatchType, error) {
func createPatch(target *resource.Info, current runtime.Object, force3WayMergePatch bool) ([]byte, types.PatchType, error) {
oldData, err := json.Marshal(current)
if err != nil {
return nil, types.StrategicMergePatchType, errors.Wrap(err, "serializing current configuration")
@ -645,7 +646,7 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P
// Unstructured objects, such as CRDs, may not have a not registered error
// returned from ConvertToVersion. Anything that's unstructured should
// use the jsonpatch.CreateMergePatch. Strategic Merge Patch is not supported
// use the jsonmergepatch.CreateThreeWayJSONMergePatch. Strategic Merge Patch is not supported
// on objects like CRDs.
_, isUnstructured := versionedObject.(runtime.Unstructured)
@ -654,6 +655,15 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P
if isUnstructured || isCRD {
// fall back to generic JSON merge patch
// TODO Helm 4: the logic controlled by this flag should become the default.
// The flag can then be removed from all the commands where it is accepted: install, upgrade, & rollback.
if force3WayMergePatch {
patch, err := jsonmergepatch.CreateThreeWayJSONMergePatch(oldData, newData, currentData)
return patch, types.MergePatchType, err
}
// 2-way merge patch logic that is kept to maintain backwards-compatibility for Helm 3
patch, err := jsonpatch.CreateMergePatch(oldData, newData)
return patch, types.MergePatchType, err
}
@ -667,7 +677,7 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P
return patch, types.StrategicMergePatchType, err
}
func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, force bool) error {
func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, force, force3WayMergePatch bool) error {
var (
obj runtime.Object
helper = resource.NewHelper(target.Client, target.Mapping).WithFieldManager(getManagedFieldsManager())
@ -683,7 +693,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)
} else {
patch, patchType, err := createPatch(target, currentObj)
patch, patchType, err := createPatch(target, currentObj, force3WayMergePatch)
if err != nil {
return errors.Wrap(err, "failed to create patch")
}

@ -279,7 +279,7 @@ func TestUpdate(t *testing.T) {
t.Fatal(err)
}
result, err := c.Update(first, second, false)
result, err := c.Update(first, second, false, false)
if err != nil {
t.Fatal(err)
}

@ -105,11 +105,11 @@ func (f *FailingKubeClient) WatchUntilReady(resources kube.ResourceList, d time.
}
// Update returns the configured error if set or prints
func (f *FailingKubeClient) Update(r, modified kube.ResourceList, ignoreMe bool) (*kube.Result, error) {
func (f *FailingKubeClient) Update(r, modified kube.ResourceList, force, force3WayMergePatch bool) (*kube.Result, error) {
if f.UpdateError != nil {
return &kube.Result{}, f.UpdateError
}
return f.PrintingKubeClient.Update(r, modified, ignoreMe)
return f.PrintingKubeClient.Update(r, modified, force, force3WayMergePatch)
}
// Build returns the configured error if set or prints

@ -92,7 +92,7 @@ func (p *PrintingKubeClient) WatchUntilReady(resources kube.ResourceList, _ time
}
// Update implements KubeClient Update.
func (p *PrintingKubeClient) Update(_, modified kube.ResourceList, _ bool) (*kube.Result, error) {
func (p *PrintingKubeClient) Update(_, modified kube.ResourceList, _, _ bool) (*kube.Result, error) {
_, err := io.Copy(p.Out, bufferize(modified))
if err != nil {
return nil, err

@ -54,7 +54,7 @@ type Interface interface {
// Update updates one or more resources or creates the resource
// if it doesn't exist.
Update(original, target ResourceList, force bool) (*Result, error)
Update(original, target ResourceList, force, force3WayMergePatch bool) (*Result, error)
// Build creates a resource list from a Reader.
//

Loading…
Cancel
Save