Repository: helm/helm
Pull request #8755 (2020-09-17 17:50:46)

size/S and larger requiring 2 LGTMs
Any changes labeled as `size/S` or larger should be thoroughly tested before merging and always
requires approval from 2 core maintainers. Beforehand, this only applied to `size/L` and larger.

PRs submitted by a core maintainer, regardless of size, only requires approval from one additional
maintainer. This ensures there are at least two maintainers who are aware of any significant PRs
introduced to the codebase.

Signed-off-by: Matthew Fisher <matt.fisher@microsoft.com>


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a8028dd01..ac88d13f2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -232,8 +232,9 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan
 3. Assigning reviews
     - Once a review has the `awaiting review` label, maintainers will review them as schedule
       permits. The maintainer who takes the issue should self-request a review.
-    - Any PR with the `size/large` label requires 2 review approvals from maintainers before it can
-      be merged. Those with `size/medium` or `size/small` are per the judgement of the maintainers.
+    - PRs from a community member with the label `size/S` or larger requires 2 review approvals from
+      maintainers before it can be merged. Those with `size/XS` are per the judgement of the
+      maintainers.
 4. Reviewing/Discussion
     - All reviews will be completed using Github review tool.
     - A "Comment" review should be used when there are questions about the code that should be
@@ -313,15 +314,16 @@ makes 30 lines of changes in 1 file, but it changes key functionality, it will l
 feature, but requires another 150 lines of tests to cover all cases, could be labeled as `size/S`
 even though the number of lines is greater than defined below.
 
-PRs submitted by a core maintainer, regardless of size, only requires approval from one additional
-maintainer. This ensures there are at least two maintainers who are aware of any significant PRs
-introduced to the codebase.
+Any changes from the community labeled as `size/S` or larger should be thoroughly tested before
+merging and always requires approval from 2 core maintainers. PRs submitted by a core maintainer,
+regardless of size, only requires approval from one additional maintainer. This ensures there are at
+least two maintainers who are aware of any significant PRs introduced to the codebase.
 
 | Label | Description |
 | ----- | ----------- |
 | `size/XS` | Denotes a PR that changes 0-9 lines, ignoring generated files. Very little testing may be required depending on the change. |
 | `size/S` | Denotes a PR that changes 10-29 lines, ignoring generated files. Only small amounts of manual testing may be required. |
 | `size/M` | Denotes a PR that changes 30-99 lines, ignoring generated files. Manual validation should be required. |
-| `size/L` | Denotes a PR that changes 100-499 lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. |
-| `size/XL` | Denotes a PR that changes 500-999 lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. |
-| `size/XXL` | Denotes a PR that changes 1000+ lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. |
+| `size/L` | Denotes a PR that changes 100-499 lines, ignoring generated files. |
+| `size/XL` | Denotes a PR that changes 500-999 lines, ignoring generated files. |
+| `size/XXL` | Denotes a PR that changes 1000+ lines, ignoring generated files. |
diff --git a/cmd/helm/create.go b/cmd/helm/create.go
index fe5cc540a..21a7e026c 100644
--- a/cmd/helm/create.go
+++ b/cmd/helm/create.go
@@ -107,7 +107,6 @@ func (o *createOptions) run(out io.Writer) error {
 		return chartutil.CreateFrom(cfile, filepath.Dir(o.name), lstarter)
 	}
 
-	chartutil.Stderr = out
 	_, err := chartutil.Create(chartname, filepath.Dir(o.name))
 	return err
 }
diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go
index 83590210a..e4aac6c0f 100644
--- a/cmd/helm/load_plugins.go
+++ b/cmd/helm/load_plugins.go
@@ -59,7 +59,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
 
 	found, err := plugin.FindPlugins(settings.PluginsDirectory)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "failed to load plugins: %s\n", err)
+		fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err)
 		return
 	}
 
diff --git a/cmd/helm/plugin_install.go b/cmd/helm/plugin_install.go
index 4e8ee327b..183d3dc57 100644
--- a/cmd/helm/plugin_install.go
+++ b/cmd/helm/plugin_install.go
@@ -19,7 +19,6 @@ import (
 	"fmt"
 	"io"
 
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"helm.sh/helm/v3/cmd/helm/require"
@@ -82,7 +81,7 @@ func (o *pluginInstallOptions) run(out io.Writer) error {
 	debug("loading plugin from %s", i.Path())
 	p, err := plugin.LoadDir(i.Path())
 	if err != nil {
-		return errors.Wrap(err, "plugin is installed but unusable")
+		return err
 	}
 
 	if err := runHook(p, plugin.Install); err != nil {
diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go
index 1c2162bfa..3eeb342f5 100644
--- a/cmd/helm/repo_add.go
+++ b/cmd/helm/repo_add.go
@@ -38,11 +38,11 @@ import (
 )
 
 type repoAddOptions struct {
-	name        string
-	url         string
-	username    string
-	password    string
-	forceUpdate bool
+	name     string
+	url      string
+	username string
+	password string
+	noUpdate bool
 
 	certFile              string
 	keyFile               string
@@ -51,9 +51,6 @@ type repoAddOptions struct {
 
 	repoFile  string
 	repoCache string
-
-	// Deprecated, but cannot be removed until Helm 4
-	deprecatedNoUpdate bool
 }
 
 func newRepoAddCmd(out io.Writer) *cobra.Command {
@@ -77,8 +74,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
 	f := cmd.Flags()
 	f.StringVar(&o.username, "username", "", "chart repository username")
 	f.StringVar(&o.password, "password", "", "chart repository password")
-	f.BoolVar(&o.forceUpdate, "force-update", false, "replace (overwrite) the repo if it already exists")
-	f.BoolVar(&o.deprecatedNoUpdate, "no-update", false, "Ignored. Formerly, it would disabled forced updates. It is deprecated by force-update.")
+	f.BoolVar(&o.noUpdate, "no-update", false, "raise error if repo is already registered")
 	f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
 	f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
 	f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
@@ -116,8 +112,7 @@ func (o *repoAddOptions) run(out io.Writer) error {
 		return err
 	}
 
-	// If the repo exists and --force-update was not specified, error out.
-	if !o.forceUpdate && f.Has(o.name) {
+	if o.noUpdate && f.Has(o.name) {
 		return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name)
 	}
 
diff --git a/cmd/helm/root_unix.go b/cmd/helm/root_unix.go
index 4eb0b442b..210842b35 100644
--- a/cmd/helm/root_unix.go
+++ b/cmd/helm/root_unix.go
@@ -1,5 +1,3 @@
-// +build !windows
-
 /*
 Copyright The Helm Authors.
 
diff --git a/pkg/action/action.go b/pkg/action/action.go
index 79bb4f638..071db709b 100644
--- a/pkg/action/action.go
+++ b/pkg/action/action.go
@@ -58,15 +58,14 @@ var (
 	errMissingRelease = errors.New("no release provided")
 	// errInvalidRevision indicates that an invalid release revision number was provided.
 	errInvalidRevision = errors.New("invalid release revision")
+	// errInvalidName indicates that an invalid release name was provided
+	errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53")
 	// errPending indicates that another instance of Helm is already applying an operation on a release.
 	errPending = errors.New("another operation (install/upgrade/rollback) is in progress")
 )
 
 // ValidName is a regular expression for resource names.
 //
-// DEPRECATED: This will be removed in Helm 4, and is no longer used here. See
-// pkg/chartutil.ValidateName for the replacement.
-//
 // According to the Kubernetes help text, the regular expression it uses is:
 //
 //	[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
@@ -295,7 +294,7 @@ func (c *Configuration) Now() time.Time {
 }
 
 func (c *Configuration) releaseContent(name string, version int) (*release.Release, error) {
-	if err := chartutil.ValidateReleaseName(name); err != nil {
+	if err := validateReleaseName(name); err != nil {
 		return nil, errors.Errorf("releaseContent: Release name is invalid: %s", name)
 	}
 
diff --git a/pkg/action/dependency.go b/pkg/action/dependency.go
index 4c80d0159..4a4b8ebad 100644
--- a/pkg/action/dependency.go
+++ b/pkg/action/dependency.go
@@ -21,7 +21,6 @@ import (
 	"io"
 	"os"
 	"path/filepath"
-	"strings"
 
 	"github.com/Masterminds/semver/v3"
 	"github.com/gosuri/uitable"
@@ -62,7 +61,6 @@ func (d *Dependency) List(chartpath string, out io.Writer) error {
 	return nil
 }
 
-// dependecyStatus returns a string describing the status of a dependency viz a viz the parent chart.
 func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string {
 	filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*")
 
@@ -77,40 +75,35 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, p
 	case err != nil:
 		return "bad pattern"
 	case len(archives) > 1:
-		// See if the second part is a SemVer
-		found := []string{}
-		for _, arc := range archives {
-			// we need to trip the prefix dirs and the extension off.
-			filename = strings.TrimSuffix(filepath.Base(arc), ".tgz")
-			maybeVersion := strings.TrimPrefix(filename, fmt.Sprintf("%s-", dep.Name))
-
-			if _, err := semver.StrictNewVersion(maybeVersion); err == nil {
-				// If the version parsed without an error, it is possibly a valid
-				// version.
-				found = append(found, arc)
+		return "too many matches"
+	case len(archives) == 1:
+		archive := archives[0]
+		if _, err := os.Stat(archive); err == nil {
+			c, err := loader.Load(archive)
+			if err != nil {
+				return "corrupt"
 			}
-		}
-
-		if l := len(found); l == 1 {
-			// If we get here, we do the same thing as in len(archives) == 1.
-			if r := statArchiveForStatus(found[0], dep); r != "" {
-				return r
+			if c.Name() != dep.Name {
+				return "misnamed"
 			}
 
-			// Fall through and look for directories
-		} else if l > 1 {
-			return "too many matches"
-		}
+			if c.Metadata.Version != dep.Version {
+				constraint, err := semver.NewConstraint(dep.Version)
+				if err != nil {
+					return "invalid version"
+				}
 
-		// The sanest thing to do here is to fall through and see if we have any directory
-		// matches.
+				v, err := semver.NewVersion(c.Metadata.Version)
+				if err != nil {
+					return "invalid version"
+				}
 
-	case len(archives) == 1:
-		archive := archives[0]
-		if r := statArchiveForStatus(archive, dep); r != "" {
-			return r
+				if !constraint.Check(v) {
+					return "wrong version"
+				}
+			}
+			return "ok"
 		}
-
 	}
 	// End unnecessary code.
 
@@ -144,40 +137,6 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, p
 	return "unpacked"
 }
 
-// stat an archive and return a message if the stat is successful
-//
-// This is a refactor of the code originally in dependencyStatus. It is here to
-// support legacy behavior, and should be removed in Helm 4.
-func statArchiveForStatus(archive string, dep *chart.Dependency) string {
-	if _, err := os.Stat(archive); err == nil {
-		c, err := loader.Load(archive)
-		if err != nil {
-			return "corrupt"
-		}
-		if c.Name() != dep.Name {
-			return "misnamed"
-		}
-
-		if c.Metadata.Version != dep.Version {
-			constraint, err := semver.NewConstraint(dep.Version)
-			if err != nil {
-				return "invalid version"
-			}
-
-			v, err := semver.NewVersion(c.Metadata.Version)
-			if err != nil {
-				return "invalid version"
-			}
-
-			if !constraint.Check(v) {
-				return "wrong version"
-			}
-		}
-		return "ok"
-	}
-	return ""
-}
-
 // printDependencies prints all of the dependencies in the yaml file.
 func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart.Chart) {
 	table := uitable.New()
diff --git a/pkg/action/history.go b/pkg/action/history.go
index f4043609c..a592745e9 100644
--- a/pkg/action/history.go
+++ b/pkg/action/history.go
@@ -19,7 +19,6 @@ package action
 import (
 	"github.com/pkg/errors"
 
-	"helm.sh/helm/v3/pkg/chartutil"
 	"helm.sh/helm/v3/pkg/release"
 )
 
@@ -46,7 +45,7 @@ func (h *History) Run(name string) ([]*release.Release, error) {
 		return nil, err
 	}
 
-	if err := chartutil.ValidateReleaseName(name); err != nil {
+	if err := validateReleaseName(name); err != nil {
 		return nil, errors.Errorf("release name is invalid: %s", name)
 	}
 
diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go
index 542acefae..8773b6271 100644
--- a/pkg/action/rollback.go
+++ b/pkg/action/rollback.go
@@ -24,7 +24,6 @@ import (
 
 	"github.com/pkg/errors"
 
-	"helm.sh/helm/v3/pkg/chartutil"
 	"helm.sh/helm/v3/pkg/release"
 	helmtime "helm.sh/helm/v3/pkg/time"
 )
@@ -91,7 +90,7 @@ func (r *Rollback) Run(name string) error {
 // prepareRollback finds the previous release and prepares a new release object with
 // the previous release's configuration
 func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Release, error) {
-	if err := chartutil.ValidateReleaseName(name); err != nil {
+	if err := validateReleaseName(name); err != nil {
 		return nil, nil, errors.Errorf("prepareRollback: Release name is invalid: %s", name)
 	}
 
diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go
index c466c6ee2..a51a283d6 100644
--- a/pkg/action/uninstall.go
+++ b/pkg/action/uninstall.go
@@ -22,7 +22,6 @@ import (
 
 	"github.com/pkg/errors"
 
-	"helm.sh/helm/v3/pkg/chartutil"
 	"helm.sh/helm/v3/pkg/release"
 	"helm.sh/helm/v3/pkg/releaseutil"
 	helmtime "helm.sh/helm/v3/pkg/time"
@@ -63,7 +62,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
 		return &release.UninstallReleaseResponse{Release: r}, nil
 	}
 
-	if err := chartutil.ValidateReleaseName(name); err != nil {
+	if err := validateReleaseName(name); err != nil {
 		return nil, errors.Errorf("uninstall: Release name is invalid: %s", name)
 	}
 
diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go
index c439af79d..b707e7e69 100644
--- a/pkg/action/upgrade.go
+++ b/pkg/action/upgrade.go
@@ -115,7 +115,7 @@ func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface
 	// the user doesn't have to specify both
 	u.Wait = u.Wait || u.Atomic
 
-	if err := chartutil.ValidateReleaseName(name); err != nil {
+	if err := validateReleaseName(name); err != nil {
 		return nil, errors.Errorf("release name is invalid: %s", name)
 	}
 	u.cfg.Log("preparing upgrade for %s", name)
@@ -142,6 +142,19 @@ func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface
 	return res, nil
 }
 
+func validateReleaseName(releaseName string) error {
+	if releaseName == "" {
+		return errMissingRelease
+	}
+
+	// Check length first, since that is a less expensive operation.
+	if len(releaseName) > releaseNameMaxLen || !ValidName.MatchString(releaseName) {
+		return errInvalidName
+	}
+
+	return nil
+}
+
 // prepareUpgrade builds an upgraded release for an upgrade operation.
 func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, *release.Release, error) {
 	if chart == nil {
diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go
index a3bed63a3..bd75375a4 100644
--- a/pkg/chart/chart.go
+++ b/pkg/chart/chart.go
@@ -17,7 +17,6 @@ package chart
 
 import (
 	"path/filepath"
-	"regexp"
 	"strings"
 )
 
@@ -27,9 +26,6 @@ const APIVersionV1 = "v1"
 // APIVersionV2 is the API version number for version 2.
 const APIVersionV2 = "v2"
 
-// aliasNameFormat defines the characters that are legal in an alias name.
-var aliasNameFormat = regexp.MustCompile("^[a-zA-Z0-9_-]+$")
-
 // Chart is a helm package that contains metadata, a default config, zero or more
 // optionally parameterizable templates, and zero or more charts (dependencies).
 type Chart struct {
diff --git a/pkg/chart/errors.go b/pkg/chart/errors.go
index 2fad5f370..4cb4189e6 100644
--- a/pkg/chart/errors.go
+++ b/pkg/chart/errors.go
@@ -15,16 +15,9 @@ limitations under the License.
 
 package chart
 
-import "fmt"
-
 // ValidationError represents a data validation error.
 type ValidationError string
 
 func (v ValidationError) Error() string {
 	return "validation: " + string(v)
 }
-
-// ValidationErrorf takes a message and formatting options and creates a ValidationError
-func ValidationErrorf(msg string, args ...interface{}) ValidationError {
-	return ValidationError(fmt.Sprintf(msg, args...))
-}
diff --git a/pkg/chart/metadata.go b/pkg/chart/metadata.go
index 1848eb280..96a3965b9 100644
--- a/pkg/chart/metadata.go
+++ b/pkg/chart/metadata.go
@@ -81,15 +81,6 @@ func (md *Metadata) Validate() error {
 	if !isValidChartType(md.Type) {
 		return ValidationError("chart.metadata.type must be application or library")
 	}
-
-	// Aliases need to be validated here to make sure that the alias name does
-	// not contain any illegal characters.
-	for _, dependency := range md.Dependencies {
-		if err := validateDependency(dependency); err != nil {
-			return err
-		}
-	}
-
 	// TODO validate valid semver here?
 	return nil
 }
@@ -101,13 +92,3 @@ func isValidChartType(in string) bool {
 	}
 	return false
 }
-
-// validateDependency checks for common problems with the dependency datastructure in
-// the chart. This check must be done at load time before the dependency's charts are
-// loaded.
-func validateDependency(dep *Dependency) error {
-	if len(dep.Alias) > 0 && !aliasNameFormat.MatchString(dep.Alias) {
-		return ValidationErrorf("dependency %q has disallowed characters in the alias", dep.Name)
-	}
-	return nil
-}
diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go
index 86681247e..6e382b961 100644
--- a/pkg/chartutil/create.go
+++ b/pkg/chartutil/create.go
@@ -18,11 +18,9 @@ package chartutil
 
 import (
 	"fmt"
-	"io"
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"regexp"
 	"strings"
 
 	"github.com/pkg/errors"
@@ -32,12 +30,6 @@ import (
 	"helm.sh/helm/v3/pkg/chart/loader"
 )
 
-// chartName is a regular expression for testing the supplied name of a chart.
-// This regular expression is probably stricter than it needs to be. We can relax it
-// somewhat. Newline characters, as well as $, quotes, +, parens, and % are known to be
-// problematic.
-var chartName = regexp.MustCompile("^[a-zA-Z0-9._-]+$")
-
 const (
 	// ChartfileName is the default Chart file name.
 	ChartfileName = "Chart.yaml"
@@ -71,10 +63,6 @@ const (
 	TestConnectionName = TemplatesTestsDir + sep + "test-connection.yaml"
 )
 
-// maxChartNameLength is lower than the limits we know of with certain file systems,
-// and with certain Kubernetes fields.
-const maxChartNameLength = 250
-
 const sep = string(filepath.Separator)
 
 const defaultChartfile = `apiVersion: v2
@@ -480,12 +468,6 @@ spec:
   restartPolicy: Never
 `
 
-// Stderr is an io.Writer to which error messages can be written
-//
-// In Helm 4, this will be replaced. It is needed in Helm 3 to preserve API backward
-// compatibility.
-var Stderr io.Writer = os.Stderr
-
 // CreateFrom creates a new chart, but scaffolds it from the src chart.
 func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
 	schart, err := loader.Load(src)
@@ -540,12 +522,6 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
 // error. In such a case, this will attempt to clean up by removing the
 // new chart directory.
 func Create(name, dir string) (string, error) {
-
-	// Sanity-check the name of a chart so user doesn't create one that causes problems.
-	if err := validateChartName(name); err != nil {
-		return "", err
-	}
-
 	path, err := filepath.Abs(dir)
 	if err != nil {
 		return path, err
@@ -625,8 +601,8 @@ func Create(name, dir string) (string, error) {
 
 	for _, file := range files {
 		if _, err := os.Stat(file.path); err == nil {
-			// There is no handle to a preferred output stream here.
-			fmt.Fprintf(Stderr, "WARNING: File %q already exists. Overwriting.\n", file.path)
+			// File exists and is okay. Skip it.
+			continue
 		}
 		if err := writeFile(file.path, file.content); err != nil {
 			return cdir, err
@@ -651,13 +627,3 @@ func writeFile(name string, content []byte) error {
 	}
 	return ioutil.WriteFile(name, content, 0644)
 }
-
-func validateChartName(name string) error {
-	if name == "" || len(name) > maxChartNameLength {
-		return fmt.Errorf("chart name must be between 1 and %d characters", maxChartNameLength)
-	}
-	if !chartName.MatchString(name) {
-		return fmt.Errorf("chart name must match the regular expression %q", chartName.String())
-	}
-	return nil
-}
diff --git a/pkg/chartutil/validate_name.go b/pkg/chartutil/validate_name.go
deleted file mode 100644
index 22132c80e..000000000
--- a/pkg/chartutil/validate_name.go
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
-Copyright The Helm Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package chartutil
-
-import (
-	"regexp"
-
-	"github.com/pkg/errors"
-)
-
-// validName is a regular expression for resource names.
-//
-// According to the Kubernetes help text, the regular expression it uses is:
-//
-//	[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
-//
-// This follows the above regular expression (but requires a full string match, not partial).
-//
-// The Kubernetes documentation is here, though it is not entirely correct:
-// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
-var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
-
-var (
-	// errMissingName indicates that a release (name) was not provided.
-	errMissingName = errors.New("no name provided")
-
-	// errInvalidName indicates that an invalid release name was provided
-	errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53")
-
-	// errInvalidKubernetesName indicates that the name does not meet the Kubernetes
-	// restrictions on metadata names.
-	errInvalidKubernetesName = errors.New("invalid metadata name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 253")
-)
-
-const (
-	// maxNameLen is the maximum length Helm allows for a release name
-	maxReleaseNameLen = 53
-	// maxMetadataNameLen is the maximum length Kubernetes allows for any name.
-	maxMetadataNameLen = 253
-)
-
-// ValidateReleaseName performs checks for an entry for a Helm release name
-//
-// For Helm to allow a name, it must be below a certain character count (53) and also match
-// a reguar expression.
-//
-// According to the Kubernetes help text, the regular expression it uses is:
-//
-//	[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
-//
-// This follows the above regular expression (but requires a full string match, not partial).
-//
-// The Kubernetes documentation is here, though it is not entirely correct:
-// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
-func ValidateReleaseName(name string) error {
-	// This case is preserved for backwards compatibility
-	if name == "" {
-		return errMissingName
-
-	}
-	if len(name) > maxReleaseNameLen || !validName.MatchString(name) {
-		return errInvalidName
-	}
-	return nil
-}
-
-// ValidateMetadataName validates the name field of a Kubernetes metadata object.
-//
-// Empty strings, strings longer than 253 chars, or strings that don't match the regexp
-// will fail.
-//
-// According to the Kubernetes help text, the regular expression it uses is:
-//
-//	[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
-//
-// This follows the above regular expression (but requires a full string match, not partial).
-//
-// The Kubernetes documentation is here, though it is not entirely correct:
-// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
-func ValidateMetadataName(name string) error {
-	if name == "" || len(name) > maxMetadataNameLen || !validName.MatchString(name) {
-		return errInvalidKubernetesName
-	}
-	return nil
-}
diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go
index 5de0819c4..73d645264 100644
--- a/pkg/lint/rules/template.go
+++ b/pkg/lint/rules/template.go
@@ -40,6 +40,14 @@ var (
 	releaseTimeSearch = regexp.MustCompile(`\.Release\.Time`)
 )
 
+// validName is a regular expression for names.
+//
+// This is different than action.ValidName. It conforms to the regular expression
+// `kubectl` says it uses, plus it disallows empty names.
+//
+// For details, see https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
+
 // Templates lints the templates in the Linter.
 func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) {
 	fpath := "templates/"
@@ -191,10 +199,10 @@ func validateMetadataName(obj *K8sYamlStruct) error {
 	}
 	// This will return an error if the characters do not abide by the standard OR if the
 	// name is left empty.
-	if err := chartutil.ValidateMetadataName(obj.Metadata.Name); err != nil {
-		return errors.Wrapf(err, "object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name)
+	if validName.MatchString(obj.Metadata.Name) {
+		return nil
 	}
-	return nil
+	return fmt.Errorf("object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name)
 }
 
 func validateNoCRDHooks(manifest []byte) error {
diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go
index bcbcbde93..28e50b72b 100644
--- a/pkg/plugin/installer/http_installer.go
+++ b/pkg/plugin/installer/http_installer.go
@@ -59,18 +59,6 @@ var Extractors = map[string]Extractor{
 	".tgz":    &TarGzExtractor{},
 }
 
-// Convert a media type to an extractor extension.
-//
-// This should be refactored in Helm 4, combined with the extension-based mechanism.
-func mediaTypeToExtension(mt string) (string, bool) {
-	switch strings.ToLower(mt) {
-	case "application/gzip", "application/x-gzip", "application/x-tgz", "application/x-gtar":
-		return ".tgz", true
-	default:
-		return "", false
-	}
-}
-
 // NewExtractor creates a new extractor matching the source file name
 func NewExtractor(source string) (Extractor, error) {
 	for suffix, extractor := range Extractors {
diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go
index 6f01494e5..61b49ab3b 100644
--- a/pkg/plugin/installer/installer.go
+++ b/pkg/plugin/installer/installer.go
@@ -18,7 +18,6 @@ package installer
 import (
 	"fmt"
 	"log"
-	"net/http"
 	"os"
 	"path/filepath"
 	"strings"
@@ -90,29 +89,10 @@ func isLocalReference(source string) bool {
 }
 
 // isRemoteHTTPArchive checks if the source is a http/https url and is an archive
-//
-// It works by checking whether the source looks like a URL and, if it does, running a
-// HEAD operation to see if the remote resource is a file that we understand.
 func isRemoteHTTPArchive(source string) bool {
 	if strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") {
-		res, err := http.Head(source)
-		if err != nil {
-			// If we get an error at the network layer, we can't install it. So
-			// we return false.
-			return false
-		}
-
-		// Next, we look for the content type or content disposition headers to see
-		// if they have matching extractors.
-		contentType := res.Header.Get("content-type")
-		foundSuffix, ok := mediaTypeToExtension(contentType)
-		if !ok {
-			// Media type not recognized
-			return false
-		}
-
 		for suffix := range Extractors {
-			if strings.HasSuffix(foundSuffix, suffix) {
+			if strings.HasSuffix(source, suffix) {
 				return true
 			}
 		}
diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go
index 93b5527a1..caa34fbd3 100644
--- a/pkg/plugin/plugin.go
+++ b/pkg/plugin/plugin.go
@@ -20,11 +20,9 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"regexp"
 	"runtime"
 	"strings"
 
-	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
 
 	"helm.sh/helm/v3/pkg/cli"
@@ -96,12 +94,6 @@ type Metadata struct {
 	// Downloaders field is used if the plugin supply downloader mechanism
 	// for special protocols.
 	Downloaders []Downloaders `json:"downloaders"`
-
-	// UseTunnelDeprecated indicates that this command needs a tunnel.
-	// Setting this will cause a number of side effects, such as the
-	// automatic setting of HELM_HOST.
-	// DEPRECATED and unused, but retained for backwards compatibility with Helm 2 plugins. Remove in Helm 4
-	UseTunnelDeprecated bool `json:"useTunnel,omitempty"`
 }
 
 // Plugin represents a plugin.
@@ -165,51 +157,18 @@ func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) {
 	return main, baseArgs, nil
 }
 
-// validPluginName is a regular expression that validates plugin names.
-//
-// Plugin names can only contain the ASCII characters a-z, A-Z, 0-9, ​_​ and ​-.
-var validPluginName = regexp.MustCompile("^[A-Za-z0-9_-]+$")
-
-// validatePluginData validates a plugin's YAML data.
-func validatePluginData(plug *Plugin, filepath string) error {
-	if !validPluginName.MatchString(plug.Metadata.Name) {
-		return fmt.Errorf("invalid plugin name at %q", filepath)
-	}
-	// We could also validate SemVer, executable, and other fields should we so choose.
-	return nil
-}
-
-func detectDuplicates(plugs []*Plugin) error {
-	names := map[string]string{}
-
-	for _, plug := range plugs {
-		if oldpath, ok := names[plug.Metadata.Name]; ok {
-			return fmt.Errorf(
-				"two plugins claim the name %q at %q and %q",
-				plug.Metadata.Name,
-				oldpath,
-				plug.Dir,
-			)
-		}
-		names[plug.Metadata.Name] = plug.Dir
-	}
-
-	return nil
-}
-
 // LoadDir loads a plugin from the given directory.
 func LoadDir(dirname string) (*Plugin, error) {
-	pluginfile := filepath.Join(dirname, PluginFileName)
-	data, err := ioutil.ReadFile(pluginfile)
+	data, err := ioutil.ReadFile(filepath.Join(dirname, PluginFileName))
 	if err != nil {
-		return nil, errors.Wrapf(err, "failed to read plugin at %q", pluginfile)
+		return nil, err
 	}
 
 	plug := &Plugin{Dir: dirname}
-	if err := yaml.UnmarshalStrict(data, &plug.Metadata); err != nil {
-		return nil, errors.Wrapf(err, "failed to load plugin at %q", pluginfile)
+	if err := yaml.Unmarshal(data, &plug.Metadata); err != nil {
+		return nil, err
 	}
-	return plug, validatePluginData(plug, pluginfile)
+	return plug, nil
 }
 
 // LoadAll loads all plugins found beneath the base directory.
@@ -221,7 +180,7 @@ func LoadAll(basedir string) ([]*Plugin, error) {
 	scanpath := filepath.Join(basedir, "*", PluginFileName)
 	matches, err := filepath.Glob(scanpath)
 	if err != nil {
-		return plugins, errors.Wrapf(err, "failed to find plugins in %q", scanpath)
+		return plugins, err
 	}
 
 	if matches == nil {
@@ -236,7 +195,7 @@ func LoadAll(basedir string) ([]*Plugin, error) {
 		}
 		plugins = append(plugins, p)
 	}
-	return plugins, detectDuplicates(plugins)
+	return plugins, nil
 }
 
 // FindPlugins returns a list of YAML files that describe plugins.
diff --git a/pkg/repo/index.go b/pkg/repo/index.go
index 55b984eea..6ef2cf8b5 100644
--- a/pkg/repo/index.go
+++ b/pkg/repo/index.go
@@ -77,8 +77,6 @@ func (c ChartVersions) Less(a, b int) bool {
 
 // IndexFile represents the index file in a chart repository
 type IndexFile struct {
-	// This is used ONLY for validation against chartmuseum's index files and is discarded after validation.
-	ServerInfo map[string]interface{}   `json:"serverInfo,omitempty"`
 	APIVersion string                   `json:"apiVersion"`
 	Generated  time.Time                `json:"generated"`
 	Entries    map[string]ChartVersions `json:"entries"`
@@ -230,23 +228,6 @@ type ChartVersion struct {
 	Created time.Time `json:"created,omitempty"`
 	Removed bool      `json:"removed,omitempty"`
 	Digest  string    `json:"digest,omitempty"`
-
-	// ChecksumDeprecated is deprecated in Helm 3, and therefore ignored. Helm 3 replaced
-	// this with Digest. However, with a strict YAML parser enabled, a field must be
-	// present on the struct for backwards compatibility.
-	ChecksumDeprecated string `json:"checksum,omitempty"`
-
-	// EngineDeprecated is deprecated in Helm 3, and therefore ignored. However, with a strict
-	// YAML parser enabled, this field must be present.
-	EngineDeprecated string `json:"engine,omitempty"`
-
-	// TillerVersionDeprecated is deprecated in Helm 3, and therefore ignored. However, with a strict
-	// YAML parser enabled, this field must be present.
-	TillerVersionDeprecated string `json:"tillerVersion,omitempty"`
-
-	// URLDeprecated is deprectaed in Helm 3, superseded by URLs. It is ignored. However,
-	// with a strict YAML parser enabled, this must be present on the struct.
-	URLDeprecated string `json:"url,omitempty"`
 }
 
 // IndexDirectory reads a (flat) directory and generates an index.
@@ -300,7 +281,7 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
 // This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails.
 func loadIndex(data []byte) (*IndexFile, error) {
 	i := &IndexFile{}
-	if err := yaml.UnmarshalStrict(data, i); err != nil {
+	if err := yaml.Unmarshal(data, i); err != nil {
 		return i, err
 	}
 	i.SortEntries()


Test patch:
diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go
index d358ad970..19281f3aa 100644
--- a/cmd/helm/repo_add_test.go
+++ b/cmd/helm/repo_add_test.go
@@ -65,11 +65,10 @@ func TestRepoAdd(t *testing.T) {
 	const testRepoName = "test-name"
 
 	o := &repoAddOptions{
-		name:               testRepoName,
-		url:                ts.URL(),
-		forceUpdate:        false,
-		deprecatedNoUpdate: true,
-		repoFile:           repoFile,
+		name:     testRepoName,
+		url:      ts.URL(),
+		noUpdate: true,
+		repoFile: repoFile,
 	}
 	os.Setenv(xdg.CacheHomeEnvVar, rootDir)
 
@@ -95,7 +94,7 @@ func TestRepoAdd(t *testing.T) {
 		t.Errorf("Error cache charts file was not created for repository %s", testRepoName)
 	}
 
-	o.forceUpdate = true
+	o.noUpdate = false
 
 	if err := o.run(ioutil.Discard); err != nil {
 		t.Errorf("Repository was not updated: %s", err)
@@ -131,11 +130,10 @@ func repoAddConcurrent(t *testing.T, testName, repoFile string) {
 		go func(name string) {
 			defer wg.Done()
 			o := &repoAddOptions{
-				name:               name,
-				url:                ts.URL(),
-				deprecatedNoUpdate: true,
-				forceUpdate:        false,
-				repoFile:           repoFile,
+				name:     name,
+				url:      ts.URL(),
+				noUpdate: true,
+				repoFile: repoFile,
 			}
 			if err := o.run(ioutil.Discard); err != nil {
 				t.Error(err)
diff --git a/cmd/helm/root_unix_test.go b/cmd/helm/root_unix_test.go
index b1fcfbc66..73f18ec28 100644
--- a/cmd/helm/root_unix_test.go
+++ b/cmd/helm/root_unix_test.go
@@ -1,5 +1,3 @@
-// +build !windows
-
 /*
 Copyright The Helm Authors.
 
diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go
index fedf260fb..c05b4403d 100644
--- a/pkg/action/action_test.go
+++ b/pkg/action/action_test.go
@@ -319,3 +319,40 @@ func TestGetVersionSet(t *testing.T) {
 		t.Error("Non-existent version is reported found.")
 	}
 }
+
+// TestValidName is a regression test for ValidName
+//
+// Kubernetes has strict naming conventions for resource names. This test represents
+// those conventions.
+//
+// See https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+//
+// NOTE: At the time of this writing, the docs above say that names cannot begin with
+// digits. However, `kubectl`'s regular expression explicit allows this, and
+// Kubernetes (at least as of 1.18) also accepts resources whose names begin with digits.
+func TestValidName(t *testing.T) {
+	names := map[string]bool{
+		"":                          false,
+		"foo":                       true,
+		"foo.bar1234baz.seventyone": true,
+		"FOO":                       false,
+		"123baz":                    true,
+		"foo.BAR.baz":               false,
+		"one-two":                   true,
+		"-two":                      false,
+		"one_two":                   false,
+		"a..b":                      false,
+		"%^&#$%*@^*@&#^":            false,
+		"example:com":               false,
+		"example%%com":              false,
+	}
+	for input, expectPass := range names {
+		if ValidName.MatchString(input) != expectPass {
+			st := "fail"
+			if expectPass {
+				st = "succeed"
+			}
+			t.Errorf("Expected %q to %s", input, st)
+		}
+	}
+}
diff --git a/pkg/action/dependency_test.go b/pkg/action/dependency_test.go
index b5032a377..4f3cb69a5 100644
--- a/pkg/action/dependency_test.go
+++ b/pkg/action/dependency_test.go
@@ -18,16 +18,9 @@ package action
 
 import (
 	"bytes"
-	"io/ioutil"
-	"os"
-	"path/filepath"
 	"testing"
 
-	"github.com/stretchr/testify/assert"
-
 	"helm.sh/helm/v3/internal/test"
-	"helm.sh/helm/v3/pkg/chart"
-	"helm.sh/helm/v3/pkg/chartutil"
 )
 
 func TestList(t *testing.T) {
@@ -63,99 +56,3 @@ func TestList(t *testing.T) {
 		test.AssertGoldenBytes(t, buf.Bytes(), tcase.golden)
 	}
 }
-
-// TestDependencyStatus_Dashes is a regression test to make sure that dashes in
-// chart names do not cause resolution problems.
-func TestDependencyStatus_Dashes(t *testing.T) {
-	// Make a temp dir
-	dir, err := ioutil.TempDir("", "helmtest-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(dir)
-
-	chartpath := filepath.Join(dir, "charts")
-	if err := os.MkdirAll(chartpath, 0700); err != nil {
-		t.Fatal(err)
-	}
-
-	// Add some fake charts
-	first := buildChart(withName("first-chart"))
-	_, err = chartutil.Save(first, chartpath)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	second := buildChart(withName("first-chart-second-chart"))
-	_, err = chartutil.Save(second, chartpath)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	dep := &chart.Dependency{
-		Name:    "first-chart",
-		Version: "0.1.0",
-	}
-
-	// Now try to get the deps
-	stat := NewDependency().dependencyStatus(dir, dep, first)
-	if stat != "ok" {
-		t.Errorf("Unexpected status: %q", stat)
-	}
-}
-
-func TestStatArchiveForStatus(t *testing.T) {
-	// Make a temp dir
-	dir, err := ioutil.TempDir("", "helmtest-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(dir)
-
-	chartpath := filepath.Join(dir, "charts")
-	if err := os.MkdirAll(chartpath, 0700); err != nil {
-		t.Fatal(err)
-	}
-
-	// unsaved chart
-	lilith := buildChart(withName("lilith"))
-
-	// dep referring to chart
-	dep := &chart.Dependency{
-		Name:    "lilith",
-		Version: "1.2.3",
-	}
-
-	is := assert.New(t)
-
-	lilithpath := filepath.Join(chartpath, "lilith-1.2.3.tgz")
-	is.Empty(statArchiveForStatus(lilithpath, dep))
-
-	// save the chart (version 0.1.0, because that is the default)
-	where, err := chartutil.Save(lilith, chartpath)
-	is.NoError(err)
-
-	// Should get "wrong version" because we asked for 1.2.3 and got 0.1.0
-	is.Equal("wrong version", statArchiveForStatus(where, dep))
-
-	// Break version on dep
-	dep = &chart.Dependency{
-		Name:    "lilith",
-		Version: "1.2.3.4.5",
-	}
-	is.Equal("invalid version", statArchiveForStatus(where, dep))
-
-	// Break the name
-	dep = &chart.Dependency{
-		Name:    "lilith2",
-		Version: "1.2.3",
-	}
-	is.Equal("misnamed", statArchiveForStatus(where, dep))
-
-	// Now create the right version
-	dep = &chart.Dependency{
-		Name:    "lilith",
-		Version: "0.1.0",
-	}
-	is.Equal("ok", statArchiveForStatus(where, dep))
-}
diff --git a/pkg/action/release_testing.go b/pkg/action/release_testing.go
index 2f6f5cfce..795c3c747 100644
--- a/pkg/action/release_testing.go
+++ b/pkg/action/release_testing.go
@@ -25,7 +25,6 @@ import (
 	"github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
 
-	"helm.sh/helm/v3/pkg/chartutil"
 	"helm.sh/helm/v3/pkg/release"
 )
 
@@ -52,7 +51,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
 		return nil, err
 	}
 
-	if err := chartutil.ValidateReleaseName(name); err != nil {
+	if err := validateReleaseName(name); err != nil {
 		return nil, errors.Errorf("releaseTest: Release name is invalid: %s", name)
 	}
 
diff --git a/pkg/chart/metadata_test.go b/pkg/chart/metadata_test.go
index 0c7b173dd..8b436000b 100644
--- a/pkg/chart/metadata_test.go
+++ b/pkg/chart/metadata_test.go
@@ -48,60 +48,12 @@ func TestValidate(t *testing.T) {
 			&Metadata{Name: "test", APIVersion: "v2", Version: "1.0", Type: "application"},
 			nil,
 		},
-		{
-			&Metadata{
-				Name:       "test",
-				APIVersion: "v2",
-				Version:    "1.0",
-				Type:       "application",
-				Dependencies: []*Dependency{
-					{Name: "dependency", Alias: "legal-alias"},
-				},
-			},
-			nil,
-		},
-		{
-			&Metadata{
-				Name:       "test",
-				APIVersion: "v2",
-				Version:    "1.0",
-				Type:       "application",
-				Dependencies: []*Dependency{
-					{Name: "bad", Alias: "illegal alias"},
-				},
-			},
-			ValidationError("dependency \"bad\" has disallowed characters in the alias"),
-		},
 	}
 
 	for _, tt := range tests {
 		result := tt.md.Validate()
 		if result != tt.err {
-			t.Errorf("expected '%s', got '%s'", tt.err, result)
-		}
-	}
-}
-
-func TestValidateDependency(t *testing.T) {
-	dep := &Dependency{
-		Name: "example",
-	}
-	for value, shouldFail := range map[string]bool{
-		"abcdefghijklmenopQRSTUVWXYZ-0123456780_": false,
-		"-okay":      false,
-		"_okay":      false,
-		"- bad":      true,
-		" bad":       true,
-		"bad\nvalue": true,
-		"bad ":       true,
-		"bad$":       true,
-	} {
-		dep.Alias = value
-		res := validateDependency(dep)
-		if res != nil && !shouldFail {
-			t.Errorf("Failed on case %q", dep.Alias)
-		} else if res == nil && shouldFail {
-			t.Errorf("Expected failure for %q", dep.Alias)
+			t.Errorf("expected %s, got %s", tt.err, result)
 		}
 	}
 }
diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go
index 9a473fc59..a11c45140 100644
--- a/pkg/chartutil/create_test.go
+++ b/pkg/chartutil/create_test.go
@@ -117,69 +117,3 @@ func TestCreateFrom(t *testing.T) {
 		}
 	}
 }
-
-// TestCreate_Overwrite is a regression test for making sure that files are overwritten.
-func TestCreate_Overwrite(t *testing.T) {
-	tdir, err := ioutil.TempDir("", "helm-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(tdir)
-
-	var errlog bytes.Buffer
-
-	if _, err := Create("foo", tdir); err != nil {
-		t.Fatal(err)
-	}
-
-	dir := filepath.Join(tdir, "foo")
-
-	tplname := filepath.Join(dir, "templates/hpa.yaml")
-	writeFile(tplname, []byte("FOO"))
-
-	// Now re-run the create
-	Stderr = &errlog
-	if _, err := Create("foo", tdir); err != nil {
-		t.Fatal(err)
-	}
-
-	data, err := ioutil.ReadFile(tplname)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if string(data) == "FOO" {
-		t.Fatal("File that should have been modified was not.")
-	}
-
-	if errlog.Len() == 0 {
-		t.Errorf("Expected warnings about overwriting files.")
-	}
-}
-
-func TestValidateChartName(t *testing.T) {
-	for name, shouldPass := range map[string]bool{
-		"":                              false,
-		"abcdefghijklmnopqrstuvwxyz-_.": true,
-		"ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.": true,
-		"$hello":                        false,
-		"Hellô":                         false,
-		"he%%o":                         false,
-		"he\nllo":                       false,
-
-		"abcdefghijklmnopqrstuvwxyz-_." +
-			"abcdefghijklmnopqrstuvwxyz-_." +
-			"abcdefghijklmnopqrstuvwxyz-_." +
-			"abcdefghijklmnopqrstuvwxyz-_." +
-			"abcdefghijklmnopqrstuvwxyz-_." +
-			"abcdefghijklmnopqrstuvwxyz-_." +
-			"abcdefghijklmnopqrstuvwxyz-_." +
-			"abcdefghijklmnopqrstuvwxyz-_." +
-			"abcdefghijklmnopqrstuvwxyz-_." +
-			"ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.": false,
-	} {
-		if err := validateChartName(name); (err != nil) == shouldPass {
-			t.Errorf("test for %q failed", name)
-		}
-	}
-}
diff --git a/pkg/chartutil/validate_name_test.go b/pkg/chartutil/validate_name_test.go
deleted file mode 100644
index 5f0792f94..000000000
--- a/pkg/chartutil/validate_name_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
-Copyright The Helm Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package chartutil
-
-import "testing"
-
-// TestValidateName is a regression test for ValidateName
-//
-// Kubernetes has strict naming conventions for resource names. This test represents
-// those conventions.
-//
-// See https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
-//
-// NOTE: At the time of this writing, the docs above say that names cannot begin with
-// digits. However, `kubectl`'s regular expression explicit allows this, and
-// Kubernetes (at least as of 1.18) also accepts resources whose names begin with digits.
-func TestValidateReleaseName(t *testing.T) {
-	names := map[string]bool{
-		"":                          false,
-		"foo":                       true,
-		"foo.bar1234baz.seventyone": true,
-		"FOO":                       false,
-		"123baz":                    true,
-		"foo.BAR.baz":               false,
-		"one-two":                   true,
-		"-two":                      false,
-		"one_two":                   false,
-		"a..b":                      false,
-		"%^&#$%*@^*@&#^":            false,
-		"example:com":               false,
-		"example%%com":              false,
-		"a1111111111111111111111111111111111111111111111111111111111z": false,
-	}
-	for input, expectPass := range names {
-		if err := ValidateReleaseName(input); (err == nil) != expectPass {
-			st := "fail"
-			if expectPass {
-				st = "succeed"
-			}
-			t.Errorf("Expected %q to %s", input, st)
-		}
-	}
-}
-
-func TestValidateMetadataName(t *testing.T) {
-	names := map[string]bool{
-		"":                          false,
-		"foo":                       true,
-		"foo.bar1234baz.seventyone": true,
-		"FOO":                       false,
-		"123baz":                    true,
-		"foo.BAR.baz":               false,
-		"one-two":                   true,
-		"-two":                      false,
-		"one_two":                   false,
-		"a..b":                      false,
-		"%^&#$%*@^*@&#^":            false,
-		"example:com":               false,
-		"example%%com":              false,
-		"a1111111111111111111111111111111111111111111111111111111111z": true,
-		"a1111111111111111111111111111111111111111111111111111111111z" +
-			"a1111111111111111111111111111111111111111111111111111111111z" +
-			"a1111111111111111111111111111111111111111111111111111111111z" +
-			"a1111111111111111111111111111111111111111111111111111111111z" +
-			"a1111111111111111111111111111111111111111111111111111111111z" +
-			"a1111111111111111111111111111111111111111111111111111111111z": false,
-	}
-	for input, expectPass := range names {
-		if err := ValidateMetadataName(input); (err == nil) != expectPass {
-			st := "fail"
-			if expectPass {
-				st = "succeed"
-			}
-			t.Errorf("Expected %q to %s", input, st)
-		}
-	}
-}
diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go
index b9fd3bf87..b456143a1 100644
--- a/pkg/downloader/chart_downloader_test.go
+++ b/pkg/downloader/chart_downloader_test.go
@@ -71,7 +71,7 @@ func TestResolveChartRef(t *testing.T) {
 			if tt.fail {
 				continue
 			}
-			t.Errorf("%s: failed with error %q", tt.name, err)
+			t.Errorf("%s: failed with error %s", tt.name, err)
 			continue
 		}
 		if got := u.String(); got != tt.expect {
diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go
index e89fea29d..3eb92ee77 100644
--- a/pkg/plugin/installer/http_installer_test.go
+++ b/pkg/plugin/installer/http_installer_test.go
@@ -20,13 +20,9 @@ import (
 	"bytes"
 	"compress/gzip"
 	"encoding/base64"
-	"fmt"
 	"io/ioutil"
-	"net/http"
-	"net/http/httptest"
 	"os"
 	"path/filepath"
-	"strings"
 	"syscall"
 	"testing"
 
@@ -67,24 +63,9 @@ func TestStripName(t *testing.T) {
 	}
 }
 
-func mockArchiveServer() *httptest.Server {
-	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		if !strings.HasSuffix(r.URL.Path, ".tar.gz") {
-			w.Header().Add("Content-Type", "text/html")
-			fmt.Fprintln(w, "broken")
-			return
-		}
-		w.Header().Add("Content-Type", "application/gzip")
-		fmt.Fprintln(w, "test")
-	}))
-}
-
 func TestHTTPInstaller(t *testing.T) {
 	defer ensure.HelmHome(t)()
-
-	srv := mockArchiveServer()
-	defer srv.Close()
-	source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz"
+	source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz"
 
 	if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
 		t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
@@ -130,9 +111,7 @@ func TestHTTPInstaller(t *testing.T) {
 
 func TestHTTPInstallerNonExistentVersion(t *testing.T) {
 	defer ensure.HelmHome(t)()
-	srv := mockArchiveServer()
-	defer srv.Close()
-	source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz"
+	source := "https://repo.localdomain/plugins/fake-plugin-0.0.2.tar.gz"
 
 	if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
 		t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
@@ -162,9 +141,7 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) {
 }
 
 func TestHTTPInstallerUpdate(t *testing.T) {
-	srv := mockArchiveServer()
-	defer srv.Close()
-	source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz"
+	source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz"
 	defer ensure.HelmHome(t)()
 
 	if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
@@ -330,26 +307,3 @@ func TestCleanJoin(t *testing.T) {
 	}
 
 }
-
-func TestMediaTypeToExtension(t *testing.T) {
-
-	for mt, shouldPass := range map[string]bool{
-		"":                   false,
-		"application/gzip":   true,
-		"application/x-gzip": true,
-		"application/x-tgz":  true,
-		"application/x-gtar": true,
-		"application/json":   false,
-	} {
-		ext, ok := mediaTypeToExtension(mt)
-		if ok != shouldPass {
-			t.Errorf("Media type %q failed test", mt)
-		}
-		if shouldPass && ext == "" {
-			t.Errorf("Expected an extension but got empty string")
-		}
-		if !shouldPass && len(ext) != 0 {
-			t.Error("Expected extension to be empty for unrecognized type")
-		}
-	}
-}
diff --git a/pkg/plugin/installer/installer_test.go b/pkg/plugin/installer/installer_test.go
deleted file mode 100644
index a11464924..000000000
--- a/pkg/plugin/installer/installer_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-Copyright The Helm Authors.
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package installer
-
-import "testing"
-
-func TestIsRemoteHTTPArchive(t *testing.T) {
-	srv := mockArchiveServer()
-	defer srv.Close()
-	source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz"
-
-	if isRemoteHTTPArchive("/not/a/URL") {
-		t.Errorf("Expected non-URL to return false")
-	}
-
-	if isRemoteHTTPArchive("https://127.0.0.1:123/fake/plugin-1.2.3.tgz") {
-		t.Errorf("Bad URL should not have succeeded.")
-	}
-
-	if !isRemoteHTTPArchive(source) {
-		t.Errorf("Expected %q to be a valid archive URL", source)
-	}
-
-	if isRemoteHTTPArchive(source + "-not-an-extension") {
-		t.Error("Expected media type match to fail")
-	}
-}
diff --git a/pkg/plugin/installer/local_installer_test.go b/pkg/plugin/installer/local_installer_test.go
index 96958ab09..3d9607331 100644
--- a/pkg/plugin/installer/local_installer_test.go
+++ b/pkg/plugin/installer/local_installer_test.go
@@ -37,7 +37,7 @@ func TestLocalInstaller(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	source := "../testdata/plugdir/good/echo"
+	source := "../testdata/plugdir/echo"
 	i, err := NewForSource(source, "")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
diff --git a/pkg/plugin/installer/vcs_installer_test.go b/pkg/plugin/installer/vcs_installer_test.go
index 6785264b3..b8dc6b1e2 100644
--- a/pkg/plugin/installer/vcs_installer_test.go
+++ b/pkg/plugin/installer/vcs_installer_test.go
@@ -56,7 +56,7 @@ func TestVCSInstaller(t *testing.T) {
 	}
 
 	source := "https://github.com/adamreese/helm-env"
-	testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo")
+	testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo")
 	repo := &testRepo{
 		local: testRepoPath,
 		tags:  []string{"0.1.0", "0.1.1"},
diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go
index 2c4478953..af0b61846 100644
--- a/pkg/plugin/plugin_test.go
+++ b/pkg/plugin/plugin_test.go
@@ -16,7 +16,6 @@ limitations under the License.
 package plugin // import "helm.sh/helm/v3/pkg/plugin"
 
 import (
-	"fmt"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -178,7 +177,7 @@ func TestNoMatchPrepareCommand(t *testing.T) {
 }
 
 func TestLoadDir(t *testing.T) {
-	dirname := "testdata/plugdir/good/hello"
+	dirname := "testdata/plugdir/hello"
 	plug, err := LoadDir(dirname)
 	if err != nil {
 		t.Fatalf("error loading Hello plugin: %s", err)
@@ -205,15 +204,8 @@ func TestLoadDir(t *testing.T) {
 	}
 }
 
-func TestLoadDirDuplicateEntries(t *testing.T) {
-	dirname := "testdata/plugdir/bad/duplicate-entries"
-	if _, err := LoadDir(dirname); err == nil {
-		t.Errorf("successfully loaded plugin with duplicate entries when it should've failed")
-	}
-}
-
 func TestDownloader(t *testing.T) {
-	dirname := "testdata/plugdir/good/downloader"
+	dirname := "testdata/plugdir/downloader"
 	plug, err := LoadDir(dirname)
 	if err != nil {
 		t.Fatalf("error loading Hello plugin: %s", err)
@@ -251,7 +243,7 @@ func TestLoadAll(t *testing.T) {
 		t.Fatalf("expected empty dir to have 0 plugins")
 	}
 
-	basedir := "testdata/plugdir/good"
+	basedir := "testdata/plugdir"
 	plugs, err := LoadAll(basedir)
 	if err != nil {
 		t.Fatalf("Could not load %q: %s", basedir, err)
@@ -295,7 +287,7 @@ func TestFindPlugins(t *testing.T) {
 		},
 		{
 			name:     "normal",
-			plugdirs: "./testdata/plugdir/good",
+			plugdirs: "./testdata/plugdir",
 			expected: 3,
 		},
 	}
@@ -328,51 +320,3 @@ func TestSetupEnv(t *testing.T) {
 		}
 	}
 }
-
-func TestValidatePluginData(t *testing.T) {
-	for i, item := range []struct {
-		pass bool
-		plug *Plugin
-	}{
-		{true, mockPlugin("abcdefghijklmnopqrstuvwxyz0123456789_-ABC")},
-		{true, mockPlugin("foo-bar-FOO-BAR_1234")},
-		{false, mockPlugin("foo -bar")},
-		{false, mockPlugin("$foo -bar")}, // Test leading chars
-		{false, mockPlugin("foo -bar ")}, // Test trailing chars
-		{false, mockPlugin("foo\nbar")},  // Test newline
-	} {
-		err := validatePluginData(item.plug, fmt.Sprintf("test-%d", i))
-		if item.pass && err != nil {
-			t.Errorf("failed to validate case %d: %s", i, err)
-		} else if !item.pass && err == nil {
-			t.Errorf("expected case %d to fail", i)
-		}
-	}
-}
-
-func TestDetectDuplicates(t *testing.T) {
-	plugs := []*Plugin{
-		mockPlugin("foo"),
-		mockPlugin("bar"),
-	}
-	if err := detectDuplicates(plugs); err != nil {
-		t.Error("no duplicates in the first set")
-	}
-	plugs = append(plugs, mockPlugin("foo"))
-	if err := detectDuplicates(plugs); err == nil {
-		t.Error("duplicates in the second set")
-	}
-}
-
-func mockPlugin(name string) *Plugin {
-	return &Plugin{
-		Metadata: &Metadata{
-			Name:        name,
-			Version:     "v0.1.2",
-			Usage:       "Mock plugin",
-			Description: "Mock plugin for testing",
-			Command:     "echo mock plugin",
-		},
-		Dir: "no-such-dir",
-	}
-}
diff --git a/pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml b/pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml
deleted file mode 100644
index 66498be96..000000000
--- a/pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: "duplicate-entries"
-version: "0.1.0"
-usage: "usage"
-description: |-
-  description
-command: "echo hello"
-ignoreFlags: true
-hooks:
-  install: "echo installing..."
-hooks:
-  install: "echo installing something different"
diff --git a/pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml b/pkg/plugin/testdata/plugdir/downloader/plugin.yaml
similarity index 100%
rename from pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml
rename to pkg/plugin/testdata/plugdir/downloader/plugin.yaml
diff --git a/pkg/plugin/testdata/plugdir/good/echo/plugin.yaml b/pkg/plugin/testdata/plugdir/echo/plugin.yaml
similarity index 100%
rename from pkg/plugin/testdata/plugdir/good/echo/plugin.yaml
rename to pkg/plugin/testdata/plugdir/echo/plugin.yaml
diff --git a/pkg/plugin/testdata/plugdir/good/hello/hello.sh b/pkg/plugin/testdata/plugdir/hello/hello.sh
similarity index 100%
rename from pkg/plugin/testdata/plugdir/good/hello/hello.sh
rename to pkg/plugin/testdata/plugdir/hello/hello.sh
diff --git a/pkg/plugin/testdata/plugdir/good/hello/plugin.yaml b/pkg/plugin/testdata/plugdir/hello/plugin.yaml
similarity index 85%
rename from pkg/plugin/testdata/plugdir/good/hello/plugin.yaml
rename to pkg/plugin/testdata/plugdir/hello/plugin.yaml
index 2b972da59..6a78756d3 100644
--- a/pkg/plugin/testdata/plugdir/good/hello/plugin.yaml
+++ b/pkg/plugin/testdata/plugdir/hello/plugin.yaml
@@ -5,5 +5,6 @@ description: |-
   description
 command: "$HELM_PLUGIN_SELF/hello.sh"
 ignoreFlags: true
+install: "echo installing..."
 hooks:
   install: "echo installing..."
diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go
index c22588971..466a2c306 100644
--- a/pkg/repo/index_test.go
+++ b/pkg/repo/index_test.go
@@ -35,31 +35,9 @@ import (
 )
 
 const (
-	testfile            = "testdata/local-index.yaml"
-	chartmuseumtestfile = "testdata/chartmuseum-index.yaml"
-	unorderedTestfile   = "testdata/local-index-unordered.yaml"
-	testRepo            = "test-repo"
-	indexWithDuplicates = `
-apiVersion: v1
-entries:
-  nginx:
-    - urls:
-        - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz
-      name: nginx
-      description: string
-      version: 0.2.0
-      home: https://github.com/something/else
-      digest: "sha256:1234567890abcdef"
-  nginx:
-    - urls:
-        - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz
-        - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz
-      name: alpine
-      description: string
-      version: 1.0.0
-      home: https://github.com/something
-      digest: "sha256:1234567890abcdef"
-`
+	testfile          = "testdata/local-index.yaml"
+	unorderedTestfile = "testdata/local-index-unordered.yaml"
+	testRepo          = "test-repo"
 )
 
 func TestIndexFile(t *testing.T) {
@@ -106,43 +84,15 @@ func TestIndexFile(t *testing.T) {
 }
 
 func TestLoadIndex(t *testing.T) {
-
-	tests := []struct {
-		Name     string
-		Filename string
-	}{
-		{
-			Name:     "regular index file",
-			Filename: testfile,
-		},
-		{
-			Name:     "chartmuseum index file",
-			Filename: chartmuseumtestfile,
-		},
-	}
-
-	for _, tc := range tests {
-		tc := tc
-		t.Run(tc.Name, func(t *testing.T) {
-			t.Parallel()
-			b, err := ioutil.ReadFile(tc.Filename)
-			if err != nil {
-				t.Fatal(err)
-			}
-			i, err := loadIndex(b)
-			if err != nil {
-				t.Fatal(err)
-			}
-			verifyLocalIndex(t, i)
-		})
+	b, err := ioutil.ReadFile(testfile)
+	if err != nil {
+		t.Fatal(err)
 	}
-}
-
-// TestLoadIndex_Duplicates is a regression to make sure that we don't non-deterministically allow duplicate packages.
-func TestLoadIndex_Duplicates(t *testing.T) {
-	if _, err := loadIndex([]byte(indexWithDuplicates)); err == nil {
-		t.Errorf("Expected an error when duplicate entries are present")
+	i, err := loadIndex(b)
+	if err != nil {
+		t.Fatal(err)
 	}
+	verifyLocalIndex(t, i)
 }
 
 func TestLoadIndexFile(t *testing.T) {
diff --git a/pkg/repo/testdata/chartmuseum-index.yaml b/pkg/repo/testdata/chartmuseum-index.yaml
deleted file mode 100644
index 3077596f4..000000000
--- a/pkg/repo/testdata/chartmuseum-index.yaml
+++ /dev/null
@@ -1,50 +0,0 @@
-serverInfo:
-  contextPath: /v1/helm
-apiVersion: v1
-entries:
-  nginx:
-    - urls:
-        - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz
-      name: nginx
-      description: string
-      version: 0.2.0
-      home: https://github.com/something/else
-      digest: "sha256:1234567890abcdef"
-      keywords:
-        - popular
-        - web server
-        - proxy
-    - urls:
-        - https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz
-      name: nginx
-      description: string
-      version: 0.1.0
-      home: https://github.com/something
-      digest: "sha256:1234567890abcdef"
-      keywords:
-        - popular
-        - web server
-        - proxy
-  alpine:
-    - urls:
-        - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz
-        - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz
-      name: alpine
-      description: string
-      version: 1.0.0
-      home: https://github.com/something
-      keywords:
-        - linux
-        - alpine
-        - small
-        - sumtin
-      digest: "sha256:1234567890abcdef"
-  chartWithNoURL:
-    - name: chartWithNoURL
-      description: string
-      version: 1.0.0
-      home: https://github.com/something
-      keywords:
-        - small
-        - sumtin
-      digest: "sha256:1234567890abcdef"