diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index d0aab5c18..1766f8646 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -32,6 +32,7 @@ import ( "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/gates" + "helm.sh/helm/v3/pkg/kube" kubefake "helm.sh/helm/v3/pkg/kube/fake" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/storage/driver" @@ -59,6 +60,12 @@ func warning(format string, v ...interface{}) { } func main() { + // Setting the name of the app for managedFields in the Kubernetes client. + // It is set here to the full name of "helm" so that renaming of helm to + // another name (e.g., helm2 or helm3) does not change the name of the + // manager as picked up by the automated name detection. + kube.ManagedFieldsManager = "helm" + actionConfig := new(action.Configuration) cmd, err := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) if err != nil { diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 8eed7776e..76a832b0e 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -21,6 +21,8 @@ import ( "encoding/json" "fmt" "io" + "os" + "path/filepath" "strings" "sync" "time" @@ -55,6 +57,10 @@ var ErrNoObjectsVisited = errors.New("no objects visited") var metadataAccessor = meta.NewAccessor() +// ManagedFieldsManager is the name of the manager of Kubernetes managedFields +// first introduced in Kubernetes 1.18 +var ManagedFieldsManager string + // Client represents a client capable of communicating with the Kubernetes API. type Client struct { Factory Factory @@ -206,7 +212,7 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err return err } - helper := resource.NewHelper(info.Client, info.Mapping) + helper := resource.NewHelper(info.Client, info.Mapping).WithFieldManager(getManagedFieldsManager()) if _, err := helper.Get(info.Namespace, info.Name); err != nil { if !apierrors.IsNotFound(err) { return errors.Wrap(err, "could not get information about the resource") @@ -359,6 +365,26 @@ func perform(infos ResourceList, fn func(*resource.Info) error) error { return nil } +// getManagedFieldsManager returns the manager string. If one was set it will be returned. +// Otherwise, one is calculated based on the name of the binary. +func getManagedFieldsManager() string { + + // When a manager is explicitly set use it + if ManagedFieldsManager != "" { + return ManagedFieldsManager + } + + // When no manager is set and no calling application can be found it is unknown + if len(os.Args[0]) == 0 { + return "unknown" + } + + // When there is an application that can be determined and no set manager + // use the base name. This is one of the ways Kubernetes libs handle figuring + // names out. + return filepath.Base(os.Args[0]) +} + func batchPerform(infos ResourceList, fn func(*resource.Info) error, errs chan<- error) { var kind string var wg sync.WaitGroup @@ -377,7 +403,7 @@ func batchPerform(infos ResourceList, fn func(*resource.Info) error, errs chan<- } func createResource(info *resource.Info) error { - obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) + obj, err := resource.NewHelper(info.Client, info.Mapping).WithFieldManager(getManagedFieldsManager()).Create(info.Namespace, true, info.Object) if err != nil { return err } @@ -387,7 +413,7 @@ func createResource(info *resource.Info) error { func deleteResource(info *resource.Info) error { policy := metav1.DeletePropagationBackground opts := &metav1.DeleteOptions{PropagationPolicy: &policy} - _, err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, opts) + _, err := resource.NewHelper(info.Client, info.Mapping).WithFieldManager(getManagedFieldsManager()).DeleteWithOptions(info.Namespace, info.Name, opts) return err } @@ -402,7 +428,7 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P } // Fetch the current object for the three way merge - helper := resource.NewHelper(target.Client, target.Mapping) + helper := resource.NewHelper(target.Client, target.Mapping).WithFieldManager(getManagedFieldsManager()) currentObj, err := helper.Get(target.Namespace, target.Name) if err != nil && !apierrors.IsNotFound(err) { return nil, types.StrategicMergePatchType, errors.Wrapf(err, "unable to get data for current object %s/%s", target.Namespace, target.Name) @@ -444,7 +470,7 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, force bool) error { var ( obj runtime.Object - helper = resource.NewHelper(target.Client, target.Mapping) + helper = resource.NewHelper(target.Client, target.Mapping).WithFieldManager(getManagedFieldsManager()) kind = target.Mapping.GroupVersionKind.Kind )