diff --git a/pkg/action/install.go b/pkg/action/install.go index ac57b921c..cab55309c 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -249,6 +249,11 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. return nil, errors.Wrap(err, "unable to build kubernetes objects from release manifest") } + err = resources.Visit(setMetadataVisitor(rel.Name, rel.Namespace)) + if err != nil { + return nil, err + } + // Install requires an extra validation step of checking that resources // don't already exist before we actually create resources. If we continue // forward and create the release object with resources that already exist, @@ -256,7 +261,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. // deleting the release because the manifest will be pointing at that // resource if !i.ClientOnly && !isUpgrade { - toBeUpdated, err := existingResourceConflict(resources, rel.Name) + toBeUpdated, err := existingResourceConflict(resources, rel.Name, rel.Namespace) if err != nil { return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install") } diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index be64fa618..b42192cda 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -203,6 +203,11 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest") } + err = target.Visit(setMetadataVisitor(upgradedRelease.Name, upgradedRelease.Namespace)) + if err != nil { + return upgradedRelease, err + } + // Do a basic diff using gvk + name to figure out what new resources are being created so we can validate they don't already exist existingResources := make(map[string]bool) for _, r := range current { @@ -216,7 +221,7 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea } } - toBeUpdated, err := existingResourceConflict(toBeCreated, upgradedRelease.Name) + toBeUpdated, err := existingResourceConflict(toBeCreated, upgradedRelease.Name, upgradedRelease.Namespace) if err != nil { return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with update") } diff --git a/pkg/action/validate.go b/pkg/action/validate.go index feecc4ff3..17a61863e 100644 --- a/pkg/action/validate.go +++ b/pkg/action/validate.go @@ -22,36 +22,22 @@ import ( "github.com/pkg/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/selection" "k8s.io/cli-runtime/pkg/resource" "helm.sh/helm/v3/pkg/kube" ) -var ( - managedByReq, _ = labels.NewRequirement("app.kubernetes.io/managed-by", selection.Equals, []string{"Helm"}) - accessor = meta.NewAccessor() -) - -func newReleaseSelector(release string) (labels.Selector, error) { - releaseReq, err := labels.NewRequirement("app.kubernetes.io/instance", selection.Equals, []string{release}) - if err != nil { - return nil, err - } - - return labels.Parse(fmt.Sprintf("%s,%s", releaseReq, managedByReq)) -} +var accessor = meta.NewAccessor() -func existingResourceConflict(resources kube.ResourceList, release string) (kube.ResourceList, error) { - sel, err := newReleaseSelector(release) - if err != nil { - return nil, err - } +const ( + helmReleaseNameAnnotation = "meta.helm.sh/release-name" + helmReleaseNamespaceAnnotation = "meta.helm.sh/release-namespace" +) +func existingResourceConflict(resources kube.ResourceList, releaseName, releaseNamespace string) (kube.ResourceList, error) { requireUpdate := kube.ResourceList{} - err = resources.Visit(func(info *resource.Info, err error) error { + err := resources.Visit(func(info *resource.Info, err error) error { if err != nil { return err } @@ -67,13 +53,10 @@ func existingResourceConflict(resources kube.ResourceList, release string) (kube } // Allow adoption of the resource if it already has app.kubernetes.io/instance and app.kubernetes.io/managed-by labels. - lbls, err := accessor.Labels(existing) - if err == nil { - set := labels.Set(lbls) - if sel.Matches(set) { - requireUpdate = append(requireUpdate, info) - return nil - } + anno, err := accessor.Annotations(existing) + if err == nil && anno != nil && anno[helmReleaseNameAnnotation] == releaseName && anno[helmReleaseNamespaceAnnotation] == releaseNamespace { + requireUpdate = append(requireUpdate, info) + return nil } return fmt.Errorf( @@ -83,3 +66,21 @@ func existingResourceConflict(resources kube.ResourceList, release string) (kube return requireUpdate, err } + +func setMetadataVisitor(releaseName, releaseNamespace string) resource.VisitorFunc { + return func(info *resource.Info, err error) error { + if err != nil { + return err + } + anno, err := accessor.Annotations(info.Object) + if err != nil { + return err + } + if anno == nil { + anno = make(map[string]string) + } + anno[helmReleaseNameAnnotation] = releaseName + anno[helmReleaseNamespaceAnnotation] = releaseNamespace + return accessor.SetAnnotations(info.Object, anno) + } +}